에디터 미디어 삽입 실행 분리
This commit is contained in:
190
composables/useAdminMarkdownMediaActions.js
Normal file
190
composables/useAdminMarkdownMediaActions.js
Normal file
@@ -0,0 +1,190 @@
|
||||
import { serializeImageMarkdown } from '../lib/markdown-image.js'
|
||||
import { createSelectedMediaInsertPlan } from '../lib/markdown-editor-media.js'
|
||||
import { insertMarkdownLinesAtSlashTarget } from '../lib/markdown-slash-commands.js'
|
||||
|
||||
/**
|
||||
* 관리자 Markdown 에디터의 미디어 삽입 실행 함수를 만든다.
|
||||
* @param {Object} options - 실행 옵션
|
||||
* @param {{ value: string }} options.value - 마크다운 본문 ref
|
||||
* @param {{ value: string }} options.mediaPickerTarget - 미디어 선택 대상 ref
|
||||
* @param {{ value: number|null }} options.liveSlashInsertLine - 슬래시 미디어 삽입 대기 줄 ref
|
||||
* @param {{ value: boolean }} options.isUploading - 업로드 중 여부 ref
|
||||
* @param {{ value: boolean }} options.isMediaPickerOpen - 미디어 선택 창 열림 여부 ref
|
||||
* @param {{ value: string }} options.activeMediaPickerTab - 미디어 선택 창 탭 ref
|
||||
* @param {() => Array<Object>} options.resolveCurrentSelectedMediaItems - 현재 선택 미디어 반환 함수
|
||||
* @param {(files: FileList|Array<File>) => Promise<Array<Object>>} options.uploadMediaFilesWithState - 파일 업로드 함수
|
||||
* @param {(files: FileList|Array<File>) => Promise<Array<Object>>} options.uploadFilesIntoMediaPicker - 미디어 선택 창 업로드 함수
|
||||
* @param {() => Promise<void>} options.fetchMediaItems - 미디어 목록 조회 함수
|
||||
* @param {() => void} options.closeMediaPicker - 미디어 선택 창 닫기 함수
|
||||
* @param {(snippet: string) => void} options.insertBlockSnippet - 블록 조각 삽입 함수
|
||||
* @param {(images: Array<Object>) => void} options.appendImagesToActiveGallery - 활성 갤러리 이미지 추가 함수
|
||||
* @param {(type: 'success'|'error'|'info', message: string) => void} options.showToast - 토스트 표시 함수
|
||||
* @param {(error: unknown) => string} options.resolveUploadFetchErrorMessage - 업로드 오류 문구 변환 함수
|
||||
* @returns {Object} 미디어 삽입 실행 함수 모음
|
||||
*/
|
||||
export const useAdminMarkdownMediaActions = ({
|
||||
value,
|
||||
mediaPickerTarget,
|
||||
liveSlashInsertLine,
|
||||
isUploading,
|
||||
isMediaPickerOpen,
|
||||
activeMediaPickerTab,
|
||||
resolveCurrentSelectedMediaItems,
|
||||
uploadMediaFilesWithState,
|
||||
uploadFilesIntoMediaPicker,
|
||||
fetchMediaItems,
|
||||
closeMediaPicker,
|
||||
insertBlockSnippet,
|
||||
appendImagesToActiveGallery,
|
||||
showToast,
|
||||
resolveUploadFetchErrorMessage
|
||||
}) => {
|
||||
/**
|
||||
* 이미지 마크다운 문자열을 생성한다.
|
||||
* @param {{ url: string, alt?: string, width?: string }} image - 이미지 정보
|
||||
* @returns {string} 이미지 마크다운
|
||||
*/
|
||||
const createImageMarkdown = (image) => serializeImageMarkdown(image)
|
||||
|
||||
/**
|
||||
* 지정 줄에 마크다운 줄을 삽입한다.
|
||||
* @param {number} lineIndex - 삽입 위치
|
||||
* @param {string[]} insertLines - 삽입 줄
|
||||
* @returns {void}
|
||||
*/
|
||||
const insertMarkdownAtLine = (lineIndex, insertLines) => {
|
||||
value.value = insertMarkdownLinesAtSlashTarget(value.value, lineIndex, insertLines)
|
||||
}
|
||||
|
||||
/**
|
||||
* 이미지 마크다운을 삽입한다.
|
||||
* @param {{ url: string, alt?: string, width?: string }} image - 이미지 정보
|
||||
* @returns {void}
|
||||
*/
|
||||
const insertImage = (image) => {
|
||||
insertBlockSnippet(createImageMarkdown(image))
|
||||
}
|
||||
|
||||
/**
|
||||
* 갤러리 마크다운을 삽입한다.
|
||||
* @param {Array<{ url: string, alt?: string, width?: string }>} images - 이미지 목록
|
||||
* @returns {void}
|
||||
*/
|
||||
const insertGallery = (images) => {
|
||||
if (!images.length) {
|
||||
return
|
||||
}
|
||||
|
||||
insertBlockSnippet([':::gallery', ...images.map(createImageMarkdown), ':::'].join('\n'))
|
||||
}
|
||||
|
||||
/**
|
||||
* 미디어 삽입 계획을 본문에 적용한다.
|
||||
* @param {{ kind: string, line?: number, lines?: string[], image?: Object, images?: Object[] }} plan - 삽입 계획
|
||||
* @returns {void}
|
||||
*/
|
||||
const applyMediaInsertPlan = (plan) => {
|
||||
if (plan.kind === 'insert-at-line') {
|
||||
insertMarkdownAtLine(plan.line, plan.lines)
|
||||
} else if (plan.kind === 'image') {
|
||||
insertImage(plan.image)
|
||||
} else if (plan.kind === 'active-gallery') {
|
||||
appendImagesToActiveGallery(plan.images)
|
||||
} else if (plan.kind === 'gallery') {
|
||||
insertGallery(plan.images)
|
||||
} else if (plan.kind === 'block') {
|
||||
insertBlockSnippet(plan.lines.join('\n'))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 미디어 항목 목록을 마크다운에 삽입한다.
|
||||
* @param {Array<Object>} selectedItems - 선택 또는 업로드된 미디어 항목
|
||||
* @param {string} [target] - 삽입 대상
|
||||
* @returns {void}
|
||||
*/
|
||||
const insertSelectedMediaItems = (selectedItems, target = mediaPickerTarget.value) => {
|
||||
const pendingSlashLine = liveSlashInsertLine.value
|
||||
const plan = createSelectedMediaInsertPlan({
|
||||
target,
|
||||
selectedItems,
|
||||
liveSlashLine: pendingSlashLine
|
||||
})
|
||||
|
||||
applyMediaInsertPlan(plan)
|
||||
|
||||
if (pendingSlashLine !== null) {
|
||||
liveSlashInsertLine.value = null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 선택한 미디어를 마크다운에 삽입한다.
|
||||
* @returns {void}
|
||||
*/
|
||||
const applyMediaSelection = () => {
|
||||
insertSelectedMediaItems(resolveCurrentSelectedMediaItems())
|
||||
closeMediaPicker()
|
||||
}
|
||||
|
||||
/**
|
||||
* 업로드 파일을 현재 커서 위치에 삽입한다.
|
||||
* @param {FileList|Array<File>} files - 파일 목록
|
||||
* @param {'image'|'gallery'|'video'|'audio'|'file'} [target] - 삽입 대상
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const uploadAndInsert = async (files, target = 'image') => {
|
||||
if (!files?.length || isUploading.value) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const uploadedFiles = await uploadMediaFilesWithState(files)
|
||||
|
||||
if (!uploadedFiles.length) {
|
||||
return
|
||||
}
|
||||
|
||||
insertSelectedMediaItems(uploadedFiles, target)
|
||||
|
||||
if (isMediaPickerOpen.value && activeMediaPickerTab.value === 'library') {
|
||||
await fetchMediaItems()
|
||||
}
|
||||
} catch (error) {
|
||||
showToast('error', resolveUploadFetchErrorMessage(error))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 미디어 모달 업로드 탭에서 파일을 업로드하고 라이브러리 목록을 갱신한다.
|
||||
* @param {FileList|Array<File>} files - 업로드 파일 목록
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const uploadFromMediaModal = async (files) => {
|
||||
if (!files?.length || isUploading.value) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const uploadedFiles = await uploadFilesIntoMediaPicker(files)
|
||||
|
||||
if (!uploadedFiles.length) {
|
||||
return
|
||||
}
|
||||
|
||||
showToast('success', '업로드가 완료되어 파일이 선택되었습니다.')
|
||||
} catch (error) {
|
||||
showToast('error', resolveUploadFetchErrorMessage(error))
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
insertMarkdownAtLine,
|
||||
insertImage,
|
||||
insertGallery,
|
||||
insertSelectedMediaItems,
|
||||
applyMediaSelection,
|
||||
uploadAndInsert,
|
||||
uploadFromMediaModal
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user