공개 본문 목록 편집 분리
This commit is contained in:
@@ -12,11 +12,7 @@ import {
|
||||
import {
|
||||
appendTextToMarkdownLine,
|
||||
getAppendTextForMerge,
|
||||
getMergeJunctionDisplayOffset,
|
||||
hasListMarker,
|
||||
isEmptyListMarkerLine,
|
||||
parseOrderedListMarker,
|
||||
stripListMarker
|
||||
getMergeJunctionDisplayOffset
|
||||
} from '../../lib/markdown-live-edit.js'
|
||||
import { CALLOUT_BACKGROUND_OPTIONS } from '../../lib/markdown-callout.js'
|
||||
import {
|
||||
@@ -32,6 +28,7 @@ import { useContentMarkdownImageDrag } from '../../composables/useContentMarkdow
|
||||
import { useContentMarkdownLiveSelection } from '../../composables/useContentMarkdownLiveSelection.js'
|
||||
import { useContentMarkdownFencedBlocks } from '../../composables/useContentMarkdownFencedBlocks.js'
|
||||
import { useContentMarkdownParagraphs } from '../../composables/useContentMarkdownParagraphs.js'
|
||||
import { useContentMarkdownLists } from '../../composables/useContentMarkdownLists.js'
|
||||
import ContentMarkdownCodeBlockEditor from './ContentMarkdownCodeBlockEditor.vue'
|
||||
import ProseCodeBlock from './ProseCodeBlock.vue'
|
||||
import ProseHtml from './ProseHtml.vue'
|
||||
@@ -190,6 +187,18 @@ const {
|
||||
commitInlineBlockLines: (block, replacementLines) => commitInlineBlockLines(block, replacementLines)
|
||||
})
|
||||
|
||||
const {
|
||||
getListMarkerNumber,
|
||||
onListItemInlineCommit,
|
||||
onListItemInsertBelow
|
||||
} = useContentMarkdownLists({
|
||||
content: computed(() => props.content),
|
||||
pendingFocusLine,
|
||||
pendingFocusPosition,
|
||||
commitInlineBlockLines: (block, replacementLines) => commitInlineBlockLines(block, replacementLines),
|
||||
insertBelowBlock: (block, options) => onInsertBelowBlock(block, options)
|
||||
})
|
||||
|
||||
/**
|
||||
* 빈 줄 공백 블록 높이를 반환한다.
|
||||
* @param {Object} block - 렌더링 블록
|
||||
@@ -401,64 +410,6 @@ const getBlockSourceLines = (block) => String(props.content || '').split('\n').s
|
||||
(block.meta.endLine ?? block.meta.startLine) + 1
|
||||
)
|
||||
|
||||
/**
|
||||
* 목록 항목 마크다운 줄을 만든다.
|
||||
* @param {string} value - 편집 값
|
||||
* @param {boolean} raw - 원문 모드 여부
|
||||
* @param {Object} block - 목록 블록
|
||||
* @param {number} itemIndex - 항목 인덱스
|
||||
* @returns {string} 마크다운 줄
|
||||
*/
|
||||
/**
|
||||
* 순서 목록 항목에 표시할 번호를 반환한다.
|
||||
* @param {Object} block - 목록 블록
|
||||
* @param {number} itemIndex - 항목 인덱스
|
||||
* @returns {number} 목록 번호
|
||||
*/
|
||||
const getListMarkerNumber = (block, itemIndex) => {
|
||||
if (block.listNumbers?.[itemIndex] != null) {
|
||||
return block.listNumbers[itemIndex]
|
||||
}
|
||||
|
||||
const line = getMarkdownLine(block.meta.startLine + itemIndex)
|
||||
const parsed = parseOrderedListMarker(line)
|
||||
|
||||
return parsed?.number ?? itemIndex + 1
|
||||
}
|
||||
|
||||
const formatListLine = (value, raw, block, itemIndex) => {
|
||||
if (raw) {
|
||||
if (!hasListMarker(value, block.ordered)) {
|
||||
return String(value ?? '').trim()
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
const clean = stripListMarker(value, block.ordered)
|
||||
|
||||
if (block.ordered) {
|
||||
const number = getListMarkerNumber(block, itemIndex)
|
||||
return clean ? `${number}. ${clean}` : `${number}. `
|
||||
}
|
||||
|
||||
return clean ? `- ${clean}` : '- '
|
||||
}
|
||||
|
||||
/**
|
||||
* 빈 목록 마커 줄을 만든다.
|
||||
* @param {Object} block - 목록 블록
|
||||
* @param {number} itemIndex - 항목 인덱스
|
||||
* @returns {string} 마크다운 줄
|
||||
*/
|
||||
const createEmptyListMarkerLine = (block, itemIndex) => {
|
||||
if (block.ordered) {
|
||||
return `${getListMarkerNumber(block, itemIndex)}. `
|
||||
}
|
||||
|
||||
return '- '
|
||||
}
|
||||
|
||||
/**
|
||||
* 인라인 편집 결과를 마크다운 줄로 반영한다.
|
||||
* @param {Object} block - 블록
|
||||
@@ -676,58 +627,6 @@ const onPreviewCardKeydown = (event, block) => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 목록 항목 Enter — 빈 마커 줄이면 문단으로 탈출, 내용이 있으면 아래에 빈 줄만 삽입한다.
|
||||
* @param {Object} block - 목록 블록
|
||||
* @param {number} itemIndex - 항목 인덱스
|
||||
* @param {string|{ value: string, raw?: boolean }} payload - 편집 내용
|
||||
* @returns {void}
|
||||
*/
|
||||
const onListItemInsertBelow = (block, itemIndex, payload) => {
|
||||
const { value, raw, before, after, caretAtStart, caretAtEnd } = normalizeInsertBelowPayload(payload)
|
||||
const nextLines = getBlockSourceLines(block)
|
||||
|
||||
if (caretAtStart && after.length) {
|
||||
nextLines[itemIndex] = formatListLine(after, raw, block, itemIndex)
|
||||
nextLines.splice(itemIndex, 0, createEmptyListMarkerLine(block, itemIndex))
|
||||
pendingFocusLine.value = block.meta.startLine + itemIndex
|
||||
pendingFocusPosition.value = 'start'
|
||||
commitInlineBlockLines(block, nextLines)
|
||||
return
|
||||
}
|
||||
|
||||
if (before.length && after.length) {
|
||||
nextLines[itemIndex] = formatListLine(before, raw, block, itemIndex)
|
||||
nextLines.splice(itemIndex + 1, 0, formatListLine(after, raw, block, itemIndex + 1))
|
||||
pendingFocusLine.value = block.meta.startLine + itemIndex + 1
|
||||
pendingFocusPosition.value = 'start'
|
||||
commitInlineBlockLines(block, nextLines)
|
||||
return
|
||||
}
|
||||
|
||||
if (itemIndex < nextLines.length) {
|
||||
nextLines[itemIndex] = formatListLine(value, raw, block, itemIndex)
|
||||
}
|
||||
|
||||
const committedLine = nextLines[itemIndex] ?? ''
|
||||
|
||||
if (isEmptyListMarkerLine(committedLine, block.ordered)) {
|
||||
nextLines[itemIndex] = ''
|
||||
pendingFocusLine.value = block.meta.startLine + itemIndex
|
||||
pendingFocusPosition.value = 'start'
|
||||
commitInlineBlockLines(block, nextLines)
|
||||
return
|
||||
}
|
||||
|
||||
commitInlineBlockLines(block, nextLines)
|
||||
|
||||
if (caretAtEnd || !before.length) {
|
||||
pendingFocusLine.value = (block.meta.endLine ?? block.meta.startLine) + 1
|
||||
pendingFocusPosition.value = 'start'
|
||||
onInsertBelowBlock(block, { lines: [''] })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 라이브 모드 하단 클릭 — 새 문단 추가
|
||||
* @returns {void}
|
||||
@@ -917,31 +816,6 @@ const onQuoteBlockConvertToParagraph = (block, payload) => {
|
||||
commitInlineBlockLines(block, replacementLines)
|
||||
}
|
||||
|
||||
/**
|
||||
* 목록 항목 인라인 편집 반영
|
||||
* @param {Object} block - 블록
|
||||
* @param {number} itemIndex - 항목 인덱스
|
||||
* @param {string|{ value: string, raw?: boolean }} payload - 편집 내용
|
||||
* @returns {void}
|
||||
*/
|
||||
const onListItemInlineCommit = (block, itemIndex, payload) => {
|
||||
const { value, raw } = normalizeCommitPayload(payload)
|
||||
const sourceLines = String(props.content || '').split('\n').slice(
|
||||
block.meta.startLine,
|
||||
(block.meta.endLine ?? block.meta.startLine) + 1
|
||||
)
|
||||
|
||||
const nextLines = sourceLines.map((line, index) => {
|
||||
if (index !== itemIndex) {
|
||||
return line
|
||||
}
|
||||
|
||||
return formatListLine(value, raw, block, index)
|
||||
})
|
||||
|
||||
commitInlineBlockLines(block, nextLines)
|
||||
}
|
||||
|
||||
/**
|
||||
* 라이브 모드에서 현재 줄을 삭제한다.
|
||||
* @param {number} lineIndex - 줄 번호
|
||||
|
||||
Reference in New Issue
Block a user