Files
sori.studio/components/content/ContentMarkdownInlineSegments.vue

164 lines
5.8 KiB
Vue

<script setup>
import { parseInlineSegments } from '../../lib/markdown-inline.js'
const props = defineProps({
/** 렌더링할 인라인 마크다운 텍스트 */
text: {
type: String,
default: ''
},
/** 링크에 적용할 클래스 */
linkClass: {
type: String,
default: 'text-[var(--site-accent)] underline underline-offset-4'
},
/** 줄바꿈을 br로 렌더링할지 여부 */
multiline: {
type: Boolean,
default: false
}
})
const segmentLines = computed(() => {
const source = String(props.text || '')
const normalizedSource = props.multiline
? source.replace(/<br\s*\/?>/gi, '\n')
: source
const lines = props.multiline ? normalizedSource.split('\n') : [normalizedSource]
return lines.map(parseInlineSegments)
})
/**
* 인라인 색상 클래스명을 반환한다.
* @param {string} color - 색상 이름
* @returns {string} 클래스명
*/
const getInlineColorClass = (color) => `content-markdown-inline-segments__color--${String(color || '').trim().toLowerCase()}`
</script>
<template>
<span class="content-markdown-inline-segments">
<span
v-for="(segment, segmentIndex) in segmentLines[0] || []"
:key="`inline-segment-0-${segmentIndex}`"
class="content-markdown-inline-segments__segment"
>
<strong v-if="segment.type === 'strong'">
<ContentMarkdownInlineSegments :text="segment.text" :link-class="linkClass" :multiline="multiline" />
</strong>
<em v-else-if="segment.type === 'em'">
<ContentMarkdownInlineSegments :text="segment.text" :link-class="linkClass" :multiline="multiline" />
</em>
<sub v-else-if="segment.type === 'subscript'">
<ContentMarkdownInlineSegments :text="segment.text" :link-class="linkClass" :multiline="multiline" />
</sub>
<sup v-else-if="segment.type === 'superscript'">
<ContentMarkdownInlineSegments :text="segment.text" :link-class="linkClass" :multiline="multiline" />
</sup>
<code v-else-if="segment.type === 'code'" class="content-markdown-inline-segments__inline-code rounded bg-[#252525] px-1.5 py-0.5 text-[0.9em] text-white">{{ segment.text }}</code>
<a v-else-if="segment.type === 'link'" :class="linkClass" :href="segment.href" target="_blank" rel="noreferrer">
<ContentMarkdownInlineSegments :text="segment.text" :link-class="linkClass" :multiline="multiline" />
</a>
<span
v-else-if="segment.type === 'color'"
class="content-markdown-inline-segments__color"
:class="getInlineColorClass(segment.color)"
:data-inline-color="segment.color"
>
<ContentMarkdownInlineSegments :text="segment.text" :link-class="linkClass" :multiline="multiline" />
</span>
<span v-else>{{ segment.text }}</span>
</span>
<span
v-for="(lineSegments, offsetIndex) in segmentLines.slice(1)"
:key="`inline-line-${offsetIndex + 1}`"
class="content-markdown-inline-segments__line"
>
<br>
<span
v-for="(segment, segmentIndex) in lineSegments"
:key="`inline-segment-${offsetIndex + 1}-${segmentIndex}`"
class="content-markdown-inline-segments__segment"
>
<strong v-if="segment.type === 'strong'">
<ContentMarkdownInlineSegments :text="segment.text" :link-class="linkClass" :multiline="multiline" />
</strong>
<em v-else-if="segment.type === 'em'">
<ContentMarkdownInlineSegments :text="segment.text" :link-class="linkClass" :multiline="multiline" />
</em>
<sub v-else-if="segment.type === 'subscript'">
<ContentMarkdownInlineSegments :text="segment.text" :link-class="linkClass" :multiline="multiline" />
</sub>
<sup v-else-if="segment.type === 'superscript'">
<ContentMarkdownInlineSegments :text="segment.text" :link-class="linkClass" :multiline="multiline" />
</sup>
<code v-else-if="segment.type === 'code'" class="content-markdown-inline-segments__inline-code rounded bg-[#252525] px-1.5 py-0.5 text-[0.9em] text-white">{{ segment.text }}</code>
<a v-else-if="segment.type === 'link'" :class="linkClass" :href="segment.href" target="_blank" rel="noreferrer">
<ContentMarkdownInlineSegments :text="segment.text" :link-class="linkClass" :multiline="multiline" />
</a>
<span
v-else-if="segment.type === 'color'"
class="content-markdown-inline-segments__color"
:class="getInlineColorClass(segment.color)"
:data-inline-color="segment.color"
>
<ContentMarkdownInlineSegments :text="segment.text" :link-class="linkClass" :multiline="multiline" />
</span>
<span v-else>{{ segment.text }}</span>
</span>
</span>
</span>
</template>
<style scoped>
.content-markdown-inline-segments,
.content-markdown-inline-segments__line,
.content-markdown-inline-segments__segment {
display: contents;
}
.content-markdown-inline-segments__color--gray {
color: var(--site-muted);
}
.content-markdown-inline-segments__color--blue {
color: #0055ff;
}
.content-markdown-inline-segments__color--green {
color: #16ae68;
}
.content-markdown-inline-segments__color--yellow {
color: #a16207;
}
.content-markdown-inline-segments__color--red {
color: #e60000;
}
.content-markdown-inline-segments__color--purple {
color: #8800ff;
}
:global(html[data-theme='dark']) .content-markdown-inline-segments__color--blue {
color: #7aa2ff;
}
:global(html[data-theme='dark']) .content-markdown-inline-segments__color--green {
color: #5ee0a0;
}
:global(html[data-theme='dark']) .content-markdown-inline-segments__color--yellow {
color: #facc15;
}
:global(html[data-theme='dark']) .content-markdown-inline-segments__color--red {
color: #ff6b6b;
}
:global(html[data-theme='dark']) .content-markdown-inline-segments__color--purple {
color: #c084fc;
}
</style>