import { deleteMarkdownSelectedLines, insertMarkdownBlockSnippet, insertMarkdownInlineLink, replaceMarkdownSelection, transformMarkdownSelectedLines, wrapMarkdownInlineSelection } from '../lib/markdown-source-selection.js' /** * 관리자 Markdown 에디터의 소스 선택 편집 실행 함수를 만든다. * @param {Object} options - 실행 옵션 * @param {{ value: string }} options.value - 마크다운 본문 ref * @param {() => { start: number, end: number, value: string }} options.getSelectionState - 현재 선택 상태 반환 함수 * @param {(start: number, end?: number) => void} options.setTextareaSelection - textarea 선택 적용 함수 * @returns {Object} 소스 선택 편집 실행 함수 모음 */ export const useAdminMarkdownSourceActions = ({ value, getSelectionState, setTextareaSelection }) => { /** * 계산 결과를 본문과 textarea 선택 범위에 적용한다. * @param {{ value: string, selectionStart: number, selectionEnd: number }} result - 선택 편집 결과 * @returns {void} */ const applySelectionResult = (result) => { value.value = result.value setTextareaSelection(result.selectionStart, result.selectionEnd) } /** * 선택 영역을 지정 문자열로 교체한다. * @param {string} replacement - 대체 문자열 * @param {number} [cursorOffset] - 교체 문자열 안 커서 위치 * @param {number|null} [selectionLength] - 선택 길이 * @returns {void} */ const replaceSelection = (replacement, cursorOffset = replacement.length, selectionLength = null) => { const selection = getSelectionState() applySelectionResult(replaceMarkdownSelection({ ...selection, replacement, cursorOffset, selectionLength })) } /** * 블록형 마크다운 조각을 커서 위치에 삽입한다. * @param {string} snippet - 삽입할 마크다운 * @returns {void} */ const insertBlockSnippet = (snippet) => { const selection = getSelectionState() applySelectionResult(insertMarkdownBlockSnippet({ ...selection, snippet })) } /** * 선택 텍스트를 인라인 마크다운으로 감싼다. * @param {string} prefix - 앞 표식 * @param {string} suffix - 뒤 표식 * @param {string} placeholder - 선택이 없을 때 들어갈 텍스트 * @returns {void} */ const wrapInline = (prefix, suffix, placeholder) => { const selection = getSelectionState() applySelectionResult(wrapMarkdownInlineSelection({ ...selection, prefix, suffix, placeholder })) } /** * 선택 텍스트를 링크 마크다운으로 바꾼다. * @returns {void} */ const insertInlineLink = () => { applySelectionResult(insertMarkdownInlineLink(getSelectionState())) } /** * 선택된 줄을 변환한다. * @param {(line: string) => string} transformLine - 줄 변환 함수 * @returns {void} */ const transformSelectedLines = (transformLine) => { const selection = getSelectionState() applySelectionResult(transformMarkdownSelectedLines({ ...selection, transformLine })) } /** * 소스 모드에서 현재 선택 범위가 걸친 줄을 삭제한다. * @returns {void} */ const deleteSelectedSourceLines = () => { applySelectionResult(deleteMarkdownSelectedLines(getSelectionState())) } /** * 제목 마크다운을 적용한다. * @param {number} level - 제목 레벨 * @returns {void} */ const applyHeading = (level) => { transformSelectedLines((line) => { const text = line.replace(/^#{1,6}\s+/, '').trimStart() return `${'#'.repeat(level)} ${text || '제목'}` }) } /** * 인용 마크다운을 적용한다. * @returns {void} */ const applyQuote = () => { transformSelectedLines((line) => line.startsWith('> ') ? line : `> ${line}`) } /** * 목록 마크다운을 적용한다. * @returns {void} */ const applyList = () => { transformSelectedLines((line) => line.startsWith('- ') ? line : `- ${line}`) } /** * 코드 블록을 삽입한다. * @returns {void} */ const insertCodeBlock = () => { const { start, end, value: currentValue } = getSelectionState() const selected = currentValue.slice(start, end) || 'code' replaceSelection(`\`\`\`\n${selected}\n\`\`\``, 4, selected.length) } return { replaceSelection, insertBlockSnippet, wrapInline, insertInlineLink, transformSelectedLines, deleteSelectedSourceLines, applyHeading, applyQuote, applyList, insertCodeBlock } }