라이브 콜아웃 선택 정렬 보정

This commit is contained in:
2026-06-04 15:29:45 +09:00
parent f048eaac2b
commit 67fbba3814
11 changed files with 100 additions and 117 deletions

View File

@@ -33,19 +33,13 @@ const props = defineProps({
}
})
const emit = defineEmits(['commit', 'delete-line', 'insert-below', 'merge-with-previous', 'leave-block', 'focus-line'])
const emit = defineEmits(['commit', 'delete-line', 'insert-below', 'merge-with-previous', 'leave-block'])
const bodyLines = computed(() => {
const lines = String(props.modelValue ?? '').replace(/\r/g, '').split('\n')
return lines.length ? lines : ['']
})
const bodyLineEntries = computed(() => bodyLines.value.map((text, index) => ({
text,
index,
sourceLine: props.bodySourceLine + index
})))
/**
* 콜아웃 마크다운 줄을 반영한다.
* @param {string[]} contentLines - 본문 줄
@@ -64,96 +58,26 @@ const commitCalloutLines = (contentLines) => {
}
/**
* 인라인 편집 값을 문자열로 정규화한다.
* 콜아웃 본문 문자열을 줄 목록으로 정규화한다.
* @param {string|{ value?: string }} payload - 편집 페이로드
* @returns {string} 편집 값
* @returns {string[]} 본문 줄
*/
const normalizeInlineValue = (payload) => {
if (typeof payload === 'string') {
return payload
}
const normalizeBodyLines = (payload) => {
const value = typeof payload === 'string'
? payload
: String(payload?.value ?? '')
return String(payload?.value ?? '')
const lines = String(value ?? '').replace(/\r/g, '').split('\n')
return lines.length ? lines : ['']
}
/**
* 아래 줄 삽입 페이로드를 정규화한다.
* @param {string|Object} payload - 아래 줄 삽입 페이로드
* @returns {{ value: string, before: string, after: string, caretAtStart: boolean }}
*/
const normalizeInsertPayload = (payload) => {
if (typeof payload === 'string') {
return {
value: payload,
before: '',
after: '',
caretAtStart: false
}
}
return {
value: String(payload?.value ?? ''),
before: String(payload?.before ?? ''),
after: String(payload?.after ?? ''),
caretAtStart: payload?.caretAtStart === true
}
}
/**
* 지정한 콜아웃 본문 줄에 포커스를 요청한다.
* @param {number} sourceLine - 원본 줄 번호
* @returns {void}
*/
const focusCalloutLine = (sourceLine) => {
emit('focus-line', {
line: sourceLine,
position: 'start',
offset: 0
})
}
/**
* 본문 한 줄 편집 반영
* @param {number} lineIndex - 본문 줄 인덱스
* 본문 편집 반영
* @param {string|{ value?: string }} payload - 편집 페이로드
* @returns {void}
*/
const onBodyLineCommit = (lineIndex, payload) => {
const nextLines = [...bodyLines.value]
nextLines[lineIndex] = normalizeInlineValue(payload)
commitCalloutLines(nextLines)
}
/**
* 현재 줄 아래에 콜아웃 본문 줄을 추가한다.
* @param {number} lineIndex - 본문 줄 인덱스
* @param {Object|string} payload - 아래 줄 삽입 페이로드
* @returns {void}
*/
const onBodyLineInsertBelow = (lineIndex, payload) => {
const { value, before, after, caretAtStart } = normalizeInsertPayload(payload)
const nextLines = [...bodyLines.value]
if (caretAtStart && after.length) {
nextLines[lineIndex] = after
nextLines.splice(lineIndex, 0, '')
focusCalloutLine(props.bodySourceLine + lineIndex)
commitCalloutLines(nextLines)
return
}
if (before.length && after.length) {
nextLines[lineIndex] = before
nextLines.splice(lineIndex + 1, 0, after)
focusCalloutLine(props.bodySourceLine + lineIndex + 1)
commitCalloutLines(nextLines)
return
}
nextLines[lineIndex] = value
nextLines.splice(lineIndex + 1, 0, '')
focusCalloutLine(props.bodySourceLine + lineIndex + 1)
commitCalloutLines(nextLines)
const onBodyCommit = (payload) => {
commitCalloutLines(normalizeBodyLines(payload))
}
</script>
@@ -168,27 +92,25 @@ const onBodyLineInsertBelow = (lineIndex, payload) => {
>
<div class="content-markdown-callout-editor__inner flex items-start gap-2">
<span
v-if="calloutEmojiEnabled"
class="content-markdown-callout-editor__emoji inline-flex size-9 shrink-0 items-center justify-center rounded-md text-xl text-[var(--site-text)]"
aria-hidden="true"
>
<span v-if="calloutEmojiEnabled">{{ calloutEmoji || '💡' }}</span>
<span v-else class="text-base text-[#8e9cac]">+</span>
{{ calloutEmoji || '💡' }}
</span>
<div class="content-markdown-callout-editor__body-lines min-w-0 flex-1">
<ContentMarkdownEditableInline
v-for="line in bodyLineEntries"
:key="`${blockSourceLine}-callout-line-${line.sourceLine}`"
block-class="content-markdown-callout-editor__body min-w-0 text-[15px] leading-8 text-[var(--site-text)]"
enter-mode="insert-below"
:source-line="line.sourceLine"
:model-value="line.text"
@commit="onBodyLineCommit(line.index, $event)"
@delete-line="emit('delete-line', $event)"
@insert-below="onBodyLineInsertBelow(line.index, $event)"
@merge-with-previous="emit('merge-with-previous', line.sourceLine, $event)"
@leave-block="emit('leave-block', $event)"
/>
</div>
<ContentMarkdownEditableInline
block-class="content-markdown-callout-editor__body min-w-0 flex-1 text-[15px] leading-8 text-[var(--site-text)]"
enter-mode="multiline"
plain-text
:source-line="bodySourceLine"
:source-line-count="bodyLines.length"
:model-value="modelValue"
@commit="onBodyCommit"
@delete-line="emit('delete-line', $event)"
@insert-below="emit('insert-below', $event)"
@merge-with-previous="emit('merge-with-previous', bodySourceLine, $event)"
@leave-block="emit('leave-block', $event)"
/>
</div>
</ProseCallout>
</div>