공개 본문 블록 계산 분리
This commit is contained in:
107
composables/useContentMarkdownBlocks.js
Normal file
107
composables/useContentMarkdownBlocks.js
Normal file
@@ -0,0 +1,107 @@
|
||||
import { computed, unref } from 'vue'
|
||||
import { getInArticleAdAfterBlockIds } from '../lib/content-in-article-ad.js'
|
||||
import { getContentLightboxImages } from '../lib/content-gallery-images.js'
|
||||
import { parseMarkdownBlocks } from '../lib/markdown-block-parser.js'
|
||||
import { createHeadingIdFactory } from '../lib/markdown-toc.js'
|
||||
|
||||
/**
|
||||
* 공개 Markdown 본문의 렌더링 블록 계산 상태를 만든다.
|
||||
* @param {Object} options - 계산 옵션
|
||||
* @param {import('vue').Ref<string>|string} options.content - Markdown 본문
|
||||
* @param {import('vue').Ref<boolean>|boolean} options.interactive - 관리자 라이브 편집 여부
|
||||
* @param {import('vue').Ref<string>|string} options.inArticleAdCode - 인아티클 광고 코드
|
||||
* @returns {{
|
||||
* blocks: import('vue').ComputedRef<Array<Object>>,
|
||||
* normalizedInArticleAdCode: import('vue').ComputedRef<string>,
|
||||
* inArticleAdAfterBlockIds: import('vue').ComputedRef<string[]>,
|
||||
* headingIdsByBlock: import('vue').ComputedRef<Record<string, string>>,
|
||||
* renderBlocks: import('vue').ComputedRef<Array<Object>>,
|
||||
* tailInsertBeforeLine: import('vue').ComputedRef<number>,
|
||||
* contentLightboxImages: import('vue').ComputedRef<Array<Object>>
|
||||
* }} 본문 블록 계산 결과
|
||||
*/
|
||||
export const useContentMarkdownBlocks = (options) => {
|
||||
const blocks = computed(() => parseMarkdownBlocks(String(unref(options.content) || '')))
|
||||
const normalizedInArticleAdCode = computed(() => String(unref(options.inArticleAdCode) || '').trim())
|
||||
|
||||
/**
|
||||
* 본문 인아티클 광고를 삽입할 블록 ID 목록을 반환한다.
|
||||
* @returns {string[]} 삽입 기준 블록 ID 목록
|
||||
*/
|
||||
const inArticleAdAfterBlockIds = computed(() => {
|
||||
if (unref(options.interactive) || !normalizedInArticleAdCode.value) {
|
||||
return []
|
||||
}
|
||||
|
||||
return getInArticleAdAfterBlockIds(blocks.value)
|
||||
})
|
||||
|
||||
const headingIdsByBlock = computed(() => {
|
||||
const createHeadingId = createHeadingIdFactory()
|
||||
const ids = {}
|
||||
|
||||
for (const block of blocks.value) {
|
||||
if (block.type === 'heading' && block.level >= 1 && block.level <= 3) {
|
||||
ids[block.id] = createHeadingId(block.text)
|
||||
}
|
||||
}
|
||||
|
||||
return ids
|
||||
})
|
||||
|
||||
/**
|
||||
* 공개 본문 렌더링용 블록 목록을 반환한다.
|
||||
* 인아티클 광고는 별도 v-if 대신 블록으로 삽입해 fragment 주석을 줄인다.
|
||||
* @returns {Array<Object>} 렌더링 블록 목록
|
||||
*/
|
||||
const renderBlocks = computed(() => {
|
||||
const sourceBlocks = blocks.value
|
||||
const adAfterBlockIds = inArticleAdAfterBlockIds.value
|
||||
|
||||
if (unref(options.interactive) || !adAfterBlockIds.length) {
|
||||
return sourceBlocks
|
||||
}
|
||||
|
||||
const adAfterIdSet = new Set(adAfterBlockIds)
|
||||
const result = []
|
||||
|
||||
for (const block of sourceBlocks) {
|
||||
result.push(block)
|
||||
|
||||
if (adAfterIdSet.has(block.id)) {
|
||||
result.push({
|
||||
id: `in-article-ad-after-${block.id}`,
|
||||
type: 'in-article-ad',
|
||||
meta: {
|
||||
afterBlockId: block.id
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
/** @type {import('vue').ComputedRef<number>} 문서 맨 아래 단일 이미지 삽입 줄 */
|
||||
const tailInsertBeforeLine = computed(() => {
|
||||
const lastBlock = blocks.value[blocks.value.length - 1]
|
||||
|
||||
if (typeof lastBlock?.meta?.endLine === 'number') {
|
||||
return lastBlock.meta.endLine + 1
|
||||
}
|
||||
|
||||
return String(unref(options.content) || '').split('\n').length
|
||||
})
|
||||
|
||||
const contentLightboxImages = computed(() => getContentLightboxImages(blocks.value))
|
||||
|
||||
return {
|
||||
blocks,
|
||||
normalizedInArticleAdCode,
|
||||
inArticleAdAfterBlockIds,
|
||||
headingIdsByBlock,
|
||||
renderBlocks,
|
||||
tailInsertBeforeLine,
|
||||
contentLightboxImages
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user