에디터 미디어 적용 버튼 상태 분리

This commit is contained in:
2026-07-24 18:47:43 +09:00
parent a188229489
commit 48982cfa52
7 changed files with 73 additions and 6 deletions

View File

@@ -22,6 +22,7 @@ import {
getEditorHistoryEntryValue
} from '../../lib/markdown-editor-history.js'
import {
buildMediaPickerApplyState,
buildMediaPickerEmptyMessage,
buildMediaPickerUploadHint,
buildMediaPickerPageItems,
@@ -3373,6 +3374,16 @@ const mediaPickerUploadHint = computed(() => buildMediaPickerUploadHint(
*/
const mediaPickerEmptyMessage = computed(() => buildMediaPickerEmptyMessage(mediaSearchQuery.value))
/**
* 미디어 선택 적용 버튼 상태를 반환한다.
* @returns {{ disabled: boolean, label: string }} 적용 버튼 상태
*/
const mediaPickerApplyState = computed(() => buildMediaPickerApplyState({
selectedUrls: selectedMediaUrls.value,
isUploading: isUploading.value,
isGallery: isGalleryMediaPicker.value
}))
/**
* 붙여넣은 이미지 파일을 업로드한다.
* @param {ClipboardEvent} event - 붙여넣기 이벤트
@@ -3807,10 +3818,10 @@ const handleKeydown = (event) => {
<button
class="admin-markdown-editor__media-apply rounded bg-[#15171a] px-4 py-2 text-sm font-semibold text-white disabled:cursor-not-allowed disabled:opacity-40"
type="button"
:disabled="isUploading || selectedMediaUrls.length === 0"
:disabled="mediaPickerApplyState.disabled"
@click="applyMediaSelection"
>
삽입
{{ mediaPickerApplyState.label }}
</button>
</div>
</footer>

View File

@@ -48,7 +48,7 @@
| lib/navigation-editor-tree.js | 관리자 메뉴 UI·서버 `renumberSortOrderByTree`가 쓰는 `buildNavigationEditorTree`, 관리자 상단 네비 평면 표용 `flattenNavigationEditorWrappers` |
| lib/markdown-content-normalizer.js | 관리자 Markdown-first 전환 후 레거시 블록 배열·객체 본문 값을 저장용 마크다운 문자열로 변환 |
| lib/markdown-editor-history.js | 관리자 Markdown-first 에디터 undo/redo 히스토리 항목 생성, 레거시 문자열 항목 본문 추출, 선택 범위 보정 |
| lib/markdown-editor-media.js | 관리자 Markdown-first 에디터 미디어 선택 모달의 미디어 종류 판별, 대상별 선택 가능 여부, 미리보기 URL, 업로드 MIME 허용, 선택 대상별 제목·accept·API kind·업로드 안내, 라이브러리 빈 상태 문구, 선택 순서·토글·항목 해석, 업로드 오류·한도 검증, 페이지 상태·범위·페이지 번호 계산, fenced 미디어 블록 생성 |
| lib/markdown-editor-media.js | 관리자 Markdown-first 에디터 미디어 선택 모달의 미디어 종류 판별, 대상별 선택 가능 여부, 미리보기 URL, 업로드 MIME 허용, 선택 대상별 제목·accept·API kind·업로드 안내, 라이브러리 빈 상태 문구, 적용 버튼 상태, 선택 순서·토글·항목 해석, 업로드 오류·한도 검증, 페이지 상태·범위·페이지 번호 계산, fenced 미디어 블록 생성 |
| lib/markdown-block-parser.js | 공개 Markdown 본문 문자열을 제목·문단·이미지·갤러리·fenced 블록·인용·목록·코드·표 렌더링 블록으로 변환 |
| lib/markdown-block-context.js | 관리자 Markdown textarea·라이브 편집 포커스 위치 기준 이미지·갤러리·링크·iframe·HTML·인용·콜아웃·코드·토글 블록 설정 패널 대상 판별, 인용 옵션 공통 파서 연결, 코드 fenced 블록 여닫는 펜스 쌍 판별 |
| lib/markdown-iframe-embed.js | 게시물 본문 iframe 블록의 코드 판별과 안전 속성 추출, `allowfullscreen`을 `allow=fullscreen` 권한으로 정규화 |

View File

@@ -1,5 +1,10 @@
# 업데이트 이력
## v1.11.77
- 관리자 Markdown 에디터 미디어 선택 적용 버튼 상태 계산을 공통 helper로 분리.
- 미디어 선택 적용 버튼 상태 단위 테스트 추가.
## v1.11.76
- 관리자 Markdown 에디터 미디어 선택 라이브러리의 빈 상태 문구 계산을 공통 helper로 분리.

View File

@@ -241,6 +241,27 @@ export const buildMediaPickerEmptyMessage = (searchQuery) => {
return '선택할 미디어가 없습니다. 업로드 탭에서 파일을 추가할 수 있습니다.'
}
/**
* 미디어 선택 적용 버튼 상태를 만든다.
* @param {Object} options - 적용 버튼 상태 옵션
* @param {string[]} options.selectedUrls - 선택된 URL 목록
* @param {boolean} options.isUploading - 업로드 중 여부
* @param {boolean} options.isGallery - 갤러리 다중 선택 여부
* @returns {{ disabled: boolean, label: string }} 적용 버튼 상태
*/
export const buildMediaPickerApplyState = ({
selectedUrls,
isUploading,
isGallery
}) => {
const selectedCount = Array.isArray(selectedUrls) ? selectedUrls.length : 0
return {
disabled: Boolean(isUploading) || selectedCount <= 0,
label: isGallery && selectedCount > 0 ? `${selectedCount}개 삽입` : '삽입'
}
}
/**
* 미디어 항목으로 Prose 미디어 블록을 만든다.
* @param {'video'|'audio'|'file'} blockType - 블록 유형

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "sori.studio",
"version": "1.11.76",
"version": "1.11.77",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "sori.studio",
"version": "1.11.76",
"version": "1.11.77",
"hasInstallScript": true,
"dependencies": {
"@nuxtjs/tailwindcss": "^6.14.0",

View File

@@ -1,6 +1,6 @@
{
"name": "sori.studio",
"version": "1.11.76",
"version": "1.11.77",
"private": true,
"type": "module",
"imports": {

View File

@@ -1,6 +1,7 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import {
buildMediaPickerApplyState,
buildMediaPickerEmptyMessage,
buildMediaPickerPageItems,
buildMediaPickerRangeLabel,
@@ -113,6 +114,35 @@ test('미디어 선택 라이브러리 빈 상태 문구를 계산한다', () =>
assert.equal(buildMediaPickerEmptyMessage(' car '), '「car」에 맞는 미디어가 없습니다.')
})
test('미디어 선택 적용 버튼 상태를 계산한다', () => {
assert.deepEqual(buildMediaPickerApplyState({
selectedUrls: [],
isUploading: false,
isGallery: false
}), {
disabled: true,
label: '삽입'
})
assert.deepEqual(buildMediaPickerApplyState({
selectedUrls: ['/a.png'],
isUploading: true,
isGallery: false
}), {
disabled: true,
label: '삽입'
})
assert.deepEqual(buildMediaPickerApplyState({
selectedUrls: ['/a.png', '/b.png'],
isUploading: false,
isGallery: true
}), {
disabled: false,
label: '2개 삽입'
})
})
test('미디어 항목으로 fenced 미디어 블록을 만든다', () => {
assert.deepEqual(createMediaBlockMarkdown('video', {
url: '/uploads/a.mp4',