rayine-ui/docs/components/DocExampleBlock.vue
2024-11-19 12:57:02 +08:00

114 lines
2.9 KiB
Vue

<script lang="ts" setup>
import FileTypeVue from "./icon/VscodeIconsFileTypeVue.vue"
import FileTypeTypescript from "./icon/VscodeIconsFileTypeTypescriptOfficial.vue"
import FileTypeJavascript from "./icon/VscodeIconsFileTypeJsOfficial.vue"
import TablerTerminal from "./icon/TablerTerminal.vue";
const hightlighter = useShikiHighlighter();
const slots = defineSlots<{
default?: () => VNode[];
code?: () => VNode[];
}>();
const IconComponents = {
'vue': FileTypeVue,
'vue-html': FileTypeVue,
'sh': TablerTerminal,
'ts': FileTypeTypescript,
'js': FileTypeJavascript,
}
const codeSlotContent = computed(() => {
if (slots.code) {
const slotContent = slots.code();
let contentLines = slotContent
.map(vnode => vnode.children || '')
.join('')
.replace('\n', '') // remove first line break
.split('\n');
// calculate the minimum indent
const minIndent = contentLines.reduce((min, line) => {
const match = line.match(/^(\s*)\S/);
if (match) {
return Math.min(min, match[1].length);
}
return min;
}, Infinity);
// remove the minimum indent from each line
const stringContent = contentLines.map(line => line.slice(minIndent)).join('\n');
return stringContent;
}
return '';
})
const props = defineProps({
filename: {
type: String,
default: '',
},
lang: {
type: String as PropType<keyof typeof IconComponents>,
default: '',
},
code: {
type: String,
default: '',
}
})
const { data: ast } = await useAsyncData(`${'name'}-ast-${JSON.stringify({ slots: props.slots, code: props.code })}`, async () => {
let formatted = ''
try {
// @ts-ignore
formatted = await $prettier.format(code.value, {
trailingComma: 'none',
semi: false,
singleQuote: true
})
} catch {
formatted = props.code
}
return parseMarkdown(formatted, {
highlight: {
highlighter,
theme: {
light: 'material-theme-lighter',
default: 'material-theme',
dark: 'material-theme-palenight'
}
}
})
})
</script>
<template>
<div class="border border-neutral-200 dark:border-neutral-700 rounded-lg">
<div v-if="filename" class="p-4 py-2 border-b border-neutral-200 dark:border-neutral-700">
<span class="flex items-center gap-1">
<component v-if="lang" :is="IconComponents[lang]" class="inline" />
<span class="text-sm text-neutral-500 dark:text-neutral-400">{{ filename }}</span>
</span>
</div>
<template v-if="slots.default">
<div :class="['p-4 overflow-auto', $slots.code ? 'border-b border-neutral-200 dark:border-neutral-700' : '']">
<slot></slot>
</div>
</template>
<template v-if="slots.code">
<div class="p-4 overflow-auto">
<!-- <LazyShiki class="text-sm" :lang="lang" :code="codeSlotContent" /> -->
<ContentRenderer :value="ast" v-if="ast"/>
</div>
</template>
</div>
</template>
<style scoped></style>