265 lines
8.2 KiB
JavaScript
265 lines
8.2 KiB
JavaScript
import { nextTick } from 'vue'
|
|
import {
|
|
createCalloutOptionsReplacementPlan,
|
|
createCodeOptionsReplacementPlan,
|
|
createEmbedUrlReplacementPlan,
|
|
createGalleryImageMovePlan,
|
|
createGalleryImagesAppendPlan,
|
|
createLiveInlineColorValue,
|
|
createMediaImageRemovePlan,
|
|
createMediaImagesReplacementPlan,
|
|
createMediaImageUpdatePlan,
|
|
createMediaUseAltPatch,
|
|
createQuoteBackgroundValue,
|
|
createSourceInlineColorValue,
|
|
createToggleOptionsReplacementPlan
|
|
} from '../lib/markdown-editor-block-options.js'
|
|
|
|
/**
|
|
* 관리자 Markdown 블록 패널의 옵션 적용 함수를 만든다.
|
|
* @param {Object} options - 블록 액션 옵션
|
|
* @param {import('vue').Ref<string>} options.value - Markdown 본문 ref
|
|
* @param {import('vue').Ref<Object|null>} options.activeMediaBlock - 현재 미디어 블록 ref
|
|
* @param {import('vue').Ref<Object|null>} options.activeBlockContext - 현재 활성 블록 ref
|
|
* @param {import('vue').Ref<Object|null>} options.inlineColorSelectionContext - 인라인 색상 선택 컨텍스트 ref
|
|
* @param {(plan: Object|null, focusEditor?: boolean) => boolean} options.applyLineReplacementPlan - 줄 교체 계획 적용 함수
|
|
* @param {() => void} options.ensureBlockPanelEngaged - 블록 패널 유지 함수
|
|
* @param {(images: Array<Object>) => void} options.insertGallery - 갤러리 삽입 함수
|
|
* @param {(start: number, end?: number, options?: Object) => void} options.setTextareaSelection - textarea 선택 범위 설정 함수
|
|
* @param {(focus: Object) => void} options.requestPreviewFocus - 미리보기 포커스 예약 함수
|
|
* @param {(line: number, column?: number, align?: string, offset?: number, block?: string) => void} options.focusPreviewEditableAtLine - 미리보기 줄 포커스 함수
|
|
* @returns {Object} 블록 패널 옵션 적용 함수
|
|
*/
|
|
export const useAdminMarkdownBlockActions = (options) => {
|
|
/**
|
|
* 줄 교체 계획을 블록 패널 유지와 함께 적용한다.
|
|
* @param {Object|null} plan - 줄 교체 계획
|
|
* @param {boolean} [engage=true] - 패널 유지 여부
|
|
* @returns {boolean} 적용 여부
|
|
*/
|
|
const applyPanelLineReplacementPlan = (plan, engage = true) => {
|
|
if (!plan) {
|
|
return false
|
|
}
|
|
|
|
if (engage) {
|
|
options.ensureBlockPanelEngaged()
|
|
}
|
|
|
|
return options.applyLineReplacementPlan(plan, false)
|
|
}
|
|
|
|
/**
|
|
* 현재 미디어 블록을 이미지 목록 기준으로 다시 작성한다.
|
|
* @param {Array<{ alt: string, url: string, width?: string }>} images - 이미지 목록
|
|
* @returns {void}
|
|
*/
|
|
const replaceActiveMediaImages = (images) => {
|
|
options.applyLineReplacementPlan(
|
|
createMediaImagesReplacementPlan(options.activeMediaBlock.value, images),
|
|
false
|
|
)
|
|
}
|
|
|
|
/**
|
|
* 현재 미디어 블록의 특정 이미지를 수정한다.
|
|
* @param {number} imageIndex - 이미지 인덱스
|
|
* @param {Partial<{ alt: string, url: string, width: string, pixelWidth: number|null }>} patch - 변경 값
|
|
* @returns {void}
|
|
*/
|
|
const updateActiveMediaImage = (imageIndex, patch) => {
|
|
applyPanelLineReplacementPlan(
|
|
createMediaImageUpdatePlan(options.activeMediaBlock.value, imageIndex, patch)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* 현재 미디어 이미지의 파일명 캡션 사용 여부를 바꾼다.
|
|
* @param {number} imageIndex - 이미지 인덱스
|
|
* @param {boolean} enabled - 파일명 캡션 사용 여부
|
|
* @returns {void}
|
|
*/
|
|
const setActiveMediaUseAlt = (imageIndex, enabled) => {
|
|
const image = options.activeMediaBlock.value?.images[imageIndex]
|
|
const patch = createMediaUseAltPatch(image, enabled)
|
|
|
|
if (patch) {
|
|
updateActiveMediaImage(imageIndex, patch)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 현재 갤러리 이미지 순서를 바꾼다.
|
|
* @param {number} imageIndex - 이동할 이미지 인덱스
|
|
* @param {-1|1} direction - 이동 방향
|
|
* @returns {void}
|
|
*/
|
|
const moveActiveGalleryImage = (imageIndex, direction) => {
|
|
applyPanelLineReplacementPlan(
|
|
createGalleryImageMovePlan(options.activeMediaBlock.value, imageIndex, direction),
|
|
false
|
|
)
|
|
}
|
|
|
|
/**
|
|
* 현재 미디어 블록에서 이미지를 삭제한다.
|
|
* @param {number} imageIndex - 삭제할 이미지 인덱스
|
|
* @returns {void}
|
|
*/
|
|
const removeActiveMediaImage = (imageIndex) => {
|
|
applyPanelLineReplacementPlan(
|
|
createMediaImageRemovePlan(options.activeMediaBlock.value, imageIndex),
|
|
false
|
|
)
|
|
}
|
|
|
|
/**
|
|
* 현재 갤러리에 이미지를 추가한다.
|
|
* @param {Array<{ url: string, alt?: string, width?: string }>} images - 추가 이미지 목록
|
|
* @returns {void}
|
|
*/
|
|
const appendImagesToActiveGallery = (images) => {
|
|
const plan = createGalleryImagesAppendPlan(options.activeMediaBlock.value, images)
|
|
|
|
if (!plan) {
|
|
options.insertGallery(images)
|
|
return
|
|
}
|
|
|
|
options.applyLineReplacementPlan(plan, false)
|
|
}
|
|
|
|
/**
|
|
* 현재 링크·iframe·HTML 블록 값을 수정한다.
|
|
* @param {string} url - 블록 값
|
|
* @returns {void}
|
|
*/
|
|
const updateActiveEmbedUrl = (url) => {
|
|
applyPanelLineReplacementPlan(
|
|
createEmbedUrlReplacementPlan(options.activeBlockContext.value, url)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* 현재 인용 블록 배경색을 수정한다.
|
|
* @param {string} background - 배경색 옵션
|
|
* @returns {void}
|
|
*/
|
|
const updateActiveQuoteBackground = (background) => {
|
|
const nextValue = createQuoteBackgroundValue({
|
|
value: options.value.value,
|
|
block: options.activeBlockContext.value,
|
|
background
|
|
})
|
|
|
|
if (nextValue === null) {
|
|
return
|
|
}
|
|
|
|
options.ensureBlockPanelEngaged()
|
|
options.value.value = nextValue
|
|
}
|
|
|
|
/**
|
|
* 현재 콜아웃 블록 옵션을 수정한다.
|
|
* @param {Partial<{ calloutEmojiEnabled: boolean, calloutEmoji: string, calloutBackground: string, title: string }>} patch - 변경 옵션
|
|
* @returns {void}
|
|
*/
|
|
const updateActiveCalloutOptions = (patch = {}) => {
|
|
applyPanelLineReplacementPlan(
|
|
createCalloutOptionsReplacementPlan(options.activeBlockContext.value, patch)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* 현재 코드 블록 옵션을 수정한다.
|
|
* @param {Partial<{ language: string, showLineNumbers: boolean }>} patch - 변경 옵션
|
|
* @returns {void}
|
|
*/
|
|
const updateActiveCodeOptions = (patch = {}) => {
|
|
applyPanelLineReplacementPlan(
|
|
createCodeOptionsReplacementPlan(options.activeBlockContext.value, patch)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* 현재 토글 블록 옵션을 수정한다.
|
|
* @param {Partial<{ defaultOpen: boolean }>} patch - 변경 옵션
|
|
* @returns {void}
|
|
*/
|
|
const updateActiveToggleOptions = (patch = {}) => {
|
|
applyPanelLineReplacementPlan(
|
|
createToggleOptionsReplacementPlan(options.activeBlockContext.value, patch)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* 현재 선택된 텍스트에 인라인 색상을 적용한다.
|
|
* @param {string} color - 색상 이름
|
|
* @returns {void}
|
|
*/
|
|
const updateActiveInlineColor = (color) => {
|
|
const context = options.inlineColorSelectionContext.value
|
|
|
|
if (!context || context.kind !== 'inline-color') {
|
|
return
|
|
}
|
|
|
|
options.ensureBlockPanelEngaged()
|
|
|
|
const sourceResult = createSourceInlineColorValue({
|
|
value: options.value.value,
|
|
context,
|
|
color
|
|
})
|
|
|
|
if (sourceResult) {
|
|
options.value.value = sourceResult.value
|
|
options.inlineColorSelectionContext.value = null
|
|
options.setTextareaSelection(sourceResult.selectionStart, sourceResult.selectionEnd, { preserveScroll: true })
|
|
return
|
|
}
|
|
|
|
const liveResult = createLiveInlineColorValue({
|
|
value: options.value.value,
|
|
context,
|
|
color
|
|
})
|
|
|
|
if (!liveResult) {
|
|
return
|
|
}
|
|
|
|
options.value.value = liveResult.value
|
|
options.inlineColorSelectionContext.value = null
|
|
options.requestPreviewFocus({
|
|
line: liveResult.focusLine,
|
|
offset: liveResult.focusOffset
|
|
})
|
|
nextTick(() => {
|
|
options.focusPreviewEditableAtLine(
|
|
liveResult.focusLine,
|
|
0,
|
|
'auto',
|
|
liveResult.focusOffset,
|
|
'nearest'
|
|
)
|
|
})
|
|
}
|
|
|
|
return {
|
|
appendImagesToActiveGallery,
|
|
moveActiveGalleryImage,
|
|
removeActiveMediaImage,
|
|
replaceActiveMediaImages,
|
|
setActiveMediaUseAlt,
|
|
updateActiveCalloutOptions,
|
|
updateActiveCodeOptions,
|
|
updateActiveEmbedUrl,
|
|
updateActiveInlineColor,
|
|
updateActiveMediaImage,
|
|
updateActiveQuoteBackground,
|
|
updateActiveToggleOptions
|
|
}
|
|
}
|