미리보기 액션 적용 분리
This commit is contained in:
243
composables/useAdminMarkdownPreviewActions.js
Normal file
243
composables/useAdminMarkdownPreviewActions.js
Normal file
@@ -0,0 +1,243 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user