mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-17 05:28:09 +01:00
docs: add ComponentCode component
This commit is contained in:
130
docs/app/components/content/ComponentCode.vue
Normal file
130
docs/app/components/content/ComponentCode.vue
Normal file
@@ -0,0 +1,130 @@
|
||||
<script setup lang="ts">
|
||||
import { upperFirst, camelCase } from 'scule'
|
||||
import * as theme from '#build/ui'
|
||||
|
||||
const props = defineProps<{
|
||||
props?: { [key: string]: any }
|
||||
slots?: { [key: string]: any }
|
||||
}>()
|
||||
|
||||
const route = useRoute()
|
||||
const { $prettier } = useNuxtApp()
|
||||
|
||||
const camelName = camelCase(route.params.slug[route.params.slug.length - 1])
|
||||
const name = `U${upperFirst(camelName)}`
|
||||
|
||||
const componentProps = reactive({ ...(props.props || {}) })
|
||||
|
||||
const componentTheme = theme[camelName]
|
||||
// const meta = await fetchComponentMeta(name as any)
|
||||
|
||||
const options = computed(() => Object.keys(props.props || {}).map((key) => {
|
||||
// const prop = meta?.meta?.props?.find((prop: any) => prop.name === key)
|
||||
const variants = componentTheme.variants?.[key]
|
||||
const items = variants
|
||||
? Object.keys(variants).map(variant => ({
|
||||
value: variant,
|
||||
label: variant,
|
||||
chip: key === 'color' ? { color: variant } : undefined
|
||||
})).filter(variant => key === 'color' ? !['error'].includes(variant.value) : true)
|
||||
: []
|
||||
|
||||
return {
|
||||
name: key,
|
||||
label: camelCase(key),
|
||||
items
|
||||
}
|
||||
}))
|
||||
|
||||
const code = computed(() => {
|
||||
let code = `\`\`\`vue
|
||||
<template>
|
||||
<${name}`
|
||||
for (const [key, value] of Object.entries(componentProps)) {
|
||||
code += ` ${key}="${value}"`
|
||||
}
|
||||
|
||||
if (props.slots) {
|
||||
const hasOnlyDefaultSlot = props.slots && Object.keys(props.slots).length === 1 && props.slots.default
|
||||
|
||||
if (hasOnlyDefaultSlot) {
|
||||
code += `>${props.slots.default}</${name}>`
|
||||
} else {
|
||||
code += `>
|
||||
${Object.entries(props.slots).map(([key, value]) => `<template #${key}>
|
||||
${value}
|
||||
</template>`).join('\n ')}
|
||||
</${name}>`
|
||||
}
|
||||
} else {
|
||||
code += ' />'
|
||||
}
|
||||
code += `\n</template>
|
||||
\`\`\`
|
||||
`
|
||||
|
||||
return code
|
||||
})
|
||||
|
||||
const { data: ast } = await useAsyncData(`${name}-code-${JSON.stringify({ props: componentProps, slots: props.slots })}`, async () => {
|
||||
let formatted = ''
|
||||
try {
|
||||
formatted = await $prettier.format(code.value)
|
||||
} catch (e) {
|
||||
formatted = code.value
|
||||
}
|
||||
|
||||
return parseMarkdown(formatted)
|
||||
}, { watch: [code] })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="options.length" class="flex items-center gap-3 border border-gray-300 dark:border-gray-700 border-b-0 relative rounded-t-md px-4 py-2.5">
|
||||
<template v-for="option in options" :key="option.name">
|
||||
<UFormField
|
||||
:label="upperFirst(option.label)"
|
||||
size="sm"
|
||||
class="inline-flex ring ring-gray-300 dark:ring-gray-700 rounded"
|
||||
:ui="{
|
||||
wrapper: 'bg-gray-50 dark:bg-gray-800/50 rounded-l flex border-r border-gray-300 dark:border-gray-700',
|
||||
label: 'text-gray-500 dark:text-gray-400 px-2 py-1.5',
|
||||
container: 'mt-0'
|
||||
}"
|
||||
>
|
||||
<USelectMenu
|
||||
v-if="option.items?.length"
|
||||
v-model="componentProps[option.name]"
|
||||
:items="option.items"
|
||||
value-key="value"
|
||||
color="gray"
|
||||
variant="soft"
|
||||
class="rounded rounded-l-none"
|
||||
>
|
||||
<template v-if="option.name === 'color'" #leading="{ modelValue, ui }">
|
||||
<UChip
|
||||
v-if="modelValue"
|
||||
inset
|
||||
standalone
|
||||
:color="(modelValue as any)"
|
||||
:size="ui.itemLeadingChipSize()"
|
||||
:class="ui.itemLeadingChip()"
|
||||
/>
|
||||
</template>
|
||||
</USelectMenu>
|
||||
<UInput v-else v-model="componentProps[option.name]" color="gray" variant="soft" :ui="{ base: 'rounded rounded-l-none' }" />
|
||||
</UFormField>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="flex border border-b-0 border-gray-300 dark:border-gray-700 relative p-4" :class="[!options.length && 'rounded-t-md']">
|
||||
<component :is="name" v-bind="componentProps">
|
||||
<template v-for="slot in Object.keys(slots || {})" :key="slot" #[slot]>
|
||||
<ContentSlot :name="slot" unwrap="p" />
|
||||
</template>
|
||||
</component>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<MDCRenderer v-if="ast" :body="ast.body" :data="ast.data" class="[&>div>pre]:!rounded-t-none [&>div>pre]:!mt-0" />
|
||||
</template>
|
||||
@@ -1,7 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import json5 from 'json5'
|
||||
import { camelCase } from 'scule'
|
||||
import { parseMarkdown } from '@nuxtjs/mdc/runtime'
|
||||
import * as theme from '#build/ui'
|
||||
|
||||
const route = useRoute()
|
||||
@@ -14,7 +13,7 @@ function stripCompoundVariants(component) {
|
||||
if (component.compoundVariants) {
|
||||
component.compoundVariants = component.compoundVariants.filter((compoundVariant: any) => {
|
||||
if (compoundVariant.color) {
|
||||
if (!['primary', 'gray', 'black', 'white'].includes(compoundVariant.color)) {
|
||||
if (!['primary', 'gray'].includes(compoundVariant.color)) {
|
||||
strippedCompoundVariants.value = true
|
||||
|
||||
return false
|
||||
@@ -34,19 +33,19 @@ const component = computed(() => {
|
||||
return stripCompoundVariants(component)
|
||||
})
|
||||
|
||||
const { data: ast } = await useAsyncData<any>(`${name}-theme`, () => parseMarkdown(`
|
||||
const { data: ast } = await useAsyncData(`${name}-theme`, () => parseMarkdown(`
|
||||
\`\`\`yml
|
||||
${json5.stringify(component.value, null, 2).replace(/,([ |\t\n]+[}|\])])/g, '$1')}
|
||||
\`\`\`\
|
||||
|
||||
${strippedCompoundVariants.value
|
||||
? `
|
||||
::callout{icon="i-simple-icons-github" to="https://github.com/benjamincanac/ui3/blob/dev/src/theme/${name}.ts" :ui='{ "icon": "text-gray-900 dark:text-white" }'}
|
||||
::callout{icon="i-simple-icons-github" to="https://github.com/benjamincanac/ui3/blob/dev/src/theme/${name}.ts"}
|
||||
Some colors in \`compoundVariants\` are omitted for readability. Check out the source code on GitHub.
|
||||
::`
|
||||
: ''}
|
||||
|
||||
::callout{icon="i-heroicons-light-bulb"}
|
||||
::tip
|
||||
You can customize this component in your \`app.config.ts\` under \`ui.${name}\` key.
|
||||
::
|
||||
`))
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import { parseMarkdown } from '@nuxtjs/mdc/runtime'
|
||||
|
||||
const props = defineProps<{
|
||||
type: string
|
||||
}>()
|
||||
@@ -16,7 +14,7 @@ const type = computed(() => {
|
||||
return props.type
|
||||
})
|
||||
|
||||
const { data: ast } = await useAsyncData<any>(`hightlight-inline-code-` + props.type, () => parseMarkdown(`\`${type.value}\`{lang="ts-type"}`))
|
||||
const { data: ast } = await useAsyncData(`hightlight-inline-code-` + props.type, () => parseMarkdown(`\`${type.value}\`{lang="ts-type"}`))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -3,12 +3,12 @@ import json5 from 'json5'
|
||||
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const { data: ast } = await useAsyncData<any>(`icons-theme`, () => parseMarkdown(`
|
||||
const { data: ast } = await useAsyncData(`icons-theme`, () => parseMarkdown(`
|
||||
\`\`\`yml
|
||||
${json5.stringify(appConfig.ui.icons, null, 2).replace(/,([ |\t\n]+[}|\])])/g, '$1')}
|
||||
\`\`\`\
|
||||
|
||||
::callout{icon="i-heroicons-light-bulb"}
|
||||
::tip
|
||||
You can customize this component in your \`app.config.ts\` under \`ui.icons\` key.
|
||||
::
|
||||
`))
|
||||
|
||||
Reference in New Issue
Block a user