159 lines
5.3 KiB
JavaScript
159 lines
5.3 KiB
JavaScript
import { unref } from 'vue'
|
|
import {
|
|
formatQuoteLine,
|
|
getQuoteContentStartLine,
|
|
normalizeQuoteEditableValue
|
|
} from '../lib/markdown-quote.js'
|
|
|
|
/**
|
|
* 관리자 라이브 Markdown 인용 편집 이벤트 상태를 만든다.
|
|
* @param {Object} options - 인용 편집 옵션
|
|
* @param {import('vue').Ref<string>|string} options.content - Markdown 본문
|
|
* @param {import('vue').Ref<number|null>} options.pendingFocusLine - 다음 포커스 줄
|
|
* @param {import('vue').Ref<'start'|'end'|'auto'>} options.pendingFocusPosition - 다음 포커스 커서 위치
|
|
* @param {import('vue').Ref<number|null>} options.pendingFocusOffset - 다음 포커스 오프셋
|
|
* @param {(block: Object, replacementLines: string[]) => void} options.commitInlineBlockLines - 블록 줄 반영 콜백
|
|
* @param {(block: Object, options?: { lines?: string[] }) => void} options.insertBelowBlock - 블록 아래 삽입 콜백
|
|
* @returns {Object} 인용 이벤트 핸들러
|
|
*/
|
|
export const useContentMarkdownQuotes = (options) => {
|
|
/**
|
|
* commit 이벤트 페이로드를 정규화한다.
|
|
* @param {string|{ value: string, raw?: boolean }} payload - 페이로드
|
|
* @returns {{ value: string, raw: boolean }}
|
|
*/
|
|
const normalizeCommitPayload = (payload) => {
|
|
if (typeof payload === 'string') {
|
|
return { value: payload, raw: false }
|
|
}
|
|
|
|
return {
|
|
value: String(payload?.value ?? ''),
|
|
raw: payload?.raw === true
|
|
}
|
|
}
|
|
|
|
/**
|
|
* insert-below 이벤트 페이로드를 정규화한다.
|
|
* @param {string|Object} payload - 페이로드
|
|
* @returns {{ value: string, raw: boolean, before: string, after: string, caretAtStart: boolean, caretAtEnd: boolean }}
|
|
*/
|
|
const normalizeInsertBelowPayload = (payload) => {
|
|
if (typeof payload === 'string') {
|
|
return {
|
|
value: payload,
|
|
raw: false,
|
|
before: '',
|
|
after: '',
|
|
caretAtStart: false,
|
|
caretAtEnd: true
|
|
}
|
|
}
|
|
|
|
return {
|
|
value: String(payload?.value ?? ''),
|
|
raw: payload?.raw === true,
|
|
before: String(payload?.before ?? ''),
|
|
after: String(payload?.after ?? ''),
|
|
caretAtStart: payload?.caretAtStart === true,
|
|
caretAtEnd: payload?.caretAtEnd === true
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 블록에 해당하는 원본 마크다운 줄 목록을 반환한다.
|
|
* @param {Object} block - 블록
|
|
* @returns {string[]} 줄 목록
|
|
*/
|
|
const getBlockSourceLines = (block) => String(unref(options.content) || '').split('\n').slice(
|
|
block.meta.startLine,
|
|
(block.meta.endLine ?? block.meta.startLine) + 1
|
|
)
|
|
|
|
/**
|
|
* 인용 블록 원본에서 옵션 선언 줄을 반환한다.
|
|
* @param {Object} block - 인용 블록
|
|
* @returns {string[]} 옵션 선언 줄
|
|
*/
|
|
const getQuoteOptionLines = (block) => {
|
|
const contentStartLine = getQuoteContentStartLine(block)
|
|
|
|
if (contentStartLine <= block.meta.startLine) {
|
|
return []
|
|
}
|
|
|
|
return getBlockSourceLines(block).slice(0, contentStartLine - block.meta.startLine)
|
|
}
|
|
|
|
/**
|
|
* 인용 본문 문자열을 마크다운 줄로 변환한다.
|
|
* @param {Object} block - 인용 블록
|
|
* @param {string|{ value?: string }} payload - 편집 페이로드
|
|
* @returns {string[]} 인용 마크다운 줄
|
|
*/
|
|
const buildQuoteBlockLines = (block, payload) => {
|
|
const value = typeof payload === 'string'
|
|
? payload
|
|
: String(payload?.value ?? '')
|
|
const bodyLines = String(value ?? '').replace(/\r/g, '').split('\n')
|
|
const normalizedBodyLines = bodyLines.length ? bodyLines : ['']
|
|
|
|
return [
|
|
...getQuoteOptionLines(block),
|
|
...normalizedBodyLines.map((line) => formatQuoteLine(line, false))
|
|
]
|
|
}
|
|
|
|
/**
|
|
* 인용 블록 편집 반영
|
|
* @param {Object} block - 인용 블록
|
|
* @param {string|{ value?: string }} payload - 편집 페이로드
|
|
* @returns {void}
|
|
*/
|
|
const onQuoteBlockCommit = (block, payload) => {
|
|
const normalizedPayload = typeof payload === 'object' && payload !== null
|
|
? { ...payload, value: normalizeQuoteEditableValue(payload.value) }
|
|
: normalizeQuoteEditableValue(payload)
|
|
|
|
options.commitInlineBlockLines(block, buildQuoteBlockLines(block, normalizedPayload))
|
|
}
|
|
|
|
/**
|
|
* 인용 블록 마지막 줄에서 아래로 이탈한다.
|
|
* @param {Object} block - 인용 블록
|
|
* @param {string|Object} payload - insert-below 페이로드
|
|
* @returns {void}
|
|
*/
|
|
const onQuoteBlockInsertBelow = (block, payload) => {
|
|
const { value } = normalizeInsertBelowPayload(payload)
|
|
|
|
onQuoteBlockCommit(block, value)
|
|
options.pendingFocusPosition.value = 'start'
|
|
options.pendingFocusOffset.value = 0
|
|
options.insertBelowBlock(block, { lines: [''] })
|
|
}
|
|
|
|
/**
|
|
* 인용 블록을 일반 문단 줄로 되돌린다.
|
|
* @param {Object} block - 인용 블록
|
|
* @param {string|Object} payload - 편집 페이로드
|
|
* @returns {void}
|
|
*/
|
|
const onQuoteBlockConvertToParagraph = (block, payload) => {
|
|
const { value } = normalizeCommitPayload(payload)
|
|
const lines = normalizeQuoteEditableValue(value).split('\n')
|
|
const replacementLines = lines.length ? lines : ['']
|
|
|
|
options.pendingFocusLine.value = block.meta.startLine
|
|
options.pendingFocusPosition.value = 'start'
|
|
options.commitInlineBlockLines(block, replacementLines)
|
|
}
|
|
|
|
return {
|
|
buildQuoteBlockLines,
|
|
onQuoteBlockCommit,
|
|
onQuoteBlockInsertBelow,
|
|
onQuoteBlockConvertToParagraph
|
|
}
|
|
}
|