feat: create config sheet

This commit is contained in:
zernonia 2024-03-14 10:58:14 +08:00
parent c332ce1133
commit e48c4dc2e9
6 changed files with 151 additions and 15 deletions

View File

@ -0,0 +1,98 @@
<script lang="ts" setup>
import { useForm } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod'
import * as z from 'zod'
import { useConfigStore } from '@/stores/config'
import { Button } from '@/lib/registry/new-york/ui/button'
import { Input } from '@/lib/registry/new-york/ui/input'
import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/lib/registry/new-york/ui/form'
import { Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger } from '@/lib/registry/new-york/ui/sheet'
import RadixIconsGear from '~icons/radix-icons/gear'
const { codeConfig, setCodeConfig } = useConfigStore()
const formSchema = toTypedSchema(z.object({
prefix: z.string().default(''),
componentsPath: z.string().default('@/components'),
utilsPath: z.string().default('@/utils'),
}))
const { handleSubmit, setValues } = useForm({
validationSchema: formSchema,
initialValues: codeConfig.value,
})
const onSubmit = handleSubmit((values) => {
setCodeConfig(values)
setValues(values)
})
</script>
<template>
<Sheet
@update:open="(open) => {
if (open) setValues(codeConfig)
}"
>
<SheetTrigger as-child>
<Button
class="w-9 h-9"
:variant="'ghost'"
:size="'icon'"
>
<RadixIconsGear class="w-5 h-5" />
</Button>
</SheetTrigger>
<SheetContent>
<form @submit="onSubmit">
<SheetHeader>
<SheetTitle>Edit code config</SheetTitle>
<SheetDescription>
Configure how the CodeBlock should render on the site.
</SheetDescription>
</SheetHeader>
<div class="my-4">
<!-- <FormField v-slot="{ componentField }" name="prefix">
<FormItem>
<FormLabel>Prefix</FormLabel>
<FormControl>
<Input placeholder="" v-bind="componentField" />
</FormControl>
<FormDescription />
<FormMessage />
</FormItem>
</FormField> -->
<FormField v-slot="{ componentField }" name="componentsPath">
<FormItem>
<FormLabel>Components Path</FormLabel>
<FormControl>
<Input placeholder="" v-bind="componentField" />
</FormControl>
<FormDescription />
<FormMessage />
</FormItem>
</FormField>
<FormField v-slot="{ componentField }" name="utilsPath">
<FormItem>
<FormLabel>Utils Path</FormLabel>
<FormControl>
<Input placeholder="" v-bind="componentField" />
</FormControl>
<FormDescription />
<FormMessage />
</FormItem>
</FormField>
</div>
<SheetFooter>
<SheetClose as-child>
<Button type="submit">
Save changes
</Button>
</SheetClose>
</SheetFooter>
</form>
</SheetContent>
</Sheet>
</template>

View File

@ -1,6 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch } from 'vue' import { ref, watch } from 'vue'
import { codeToHtml } from 'shiki' import { codeToHtml } from 'shiki'
import MagicString from 'magic-string'
import { cssVariables } from '../config/shiki' import { cssVariables } from '../config/shiki'
import StyleSwitcher from './StyleSwitcher.vue' import StyleSwitcher from './StyleSwitcher.vue'
import ComponentLoader from './ComponentLoader.vue' import ComponentLoader from './ComponentLoader.vue'
@ -19,19 +20,19 @@ const props = withDefaults(defineProps<{
align?: 'center' | 'start' | 'end' align?: 'center' | 'start' | 'end'
}>(), { align: 'center' }) }>(), { align: 'center' })
const { style } = useConfigStore() const { style, codeConfig } = useConfigStore()
const codeString = ref('') const codeString = ref('')
const codeHtml = ref('') const codeHtml = ref('')
function transformImportPath(code: string) { function transformImportPath(code: string) {
let parsed = code const s = new MagicString(code)
parsed = parsed.replaceAll(`@/lib/registry/${style.value}`, '@/components') s.replaceAll(`@/lib/registry/${style.value}`, codeConfig.value.componentsPath)
parsed = parsed.replaceAll('@/lib/utils', '@/utils') s.replaceAll(`@/lib/utils`, codeConfig.value.utilsPath)
return parsed return s.toString()
} }
watch(style, async () => { watch([style, codeConfig], async () => {
try { try {
codeString.value = await import(`../../../src/lib/registry/${style.value}/example/${props.name}.vue?raw`).then(res => transformImportPath(res.default.trim())) codeString.value = await import(`../../../src/lib/registry/${style.value}/example/${props.name}.vue?raw`).then(res => transformImportPath(res.default.trim()))
codeHtml.value = await codeToHtml(codeString.value, { codeHtml.value = await codeToHtml(codeString.value, {

View File

@ -5,6 +5,7 @@ import { Content, useData, useRoute, useRouter } from 'vitepress'
import { type NavItem, docsConfig } from '../config/docs' import { type NavItem, docsConfig } from '../config/docs'
import Logo from '../components/Logo.vue' import Logo from '../components/Logo.vue'
import MobileNav from '../components/MobileNav.vue' import MobileNav from '../components/MobileNav.vue'
import CodeConfigCustomizer from '../components/CodeConfigCustomizer.vue'
import Kbd from '../components/Kbd.vue' import Kbd from '../components/Kbd.vue'
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator } from '@/lib/registry/default/ui/command' import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator } from '@/lib/registry/default/ui/command'
@ -126,27 +127,32 @@ watch(() => $route.path, (n) => {
</Button> </Button>
</div> </div>
<nav class="flex items-center gap-x-1"> <nav class="flex items-center">
<CodeConfigCustomizer />
<Button <Button
v-for="link in links" v-for="link in links"
:key="link.name" :key="link.name"
as="a" as="a"
class="w-9 h-9"
:href="link.href" target="_blank" :href="link.href" target="_blank"
:variant="'ghost'" :size="'icon'" :variant="'ghost'"
:size="'icon'"
> >
<component :is="link.icon" class="w-[20px] h-5" /> <component :is="link.icon" class="w-5 h-5" />
</Button> </Button>
<ClientOnly> <ClientOnly>
<Button <Button
class="flex items-center justify-center" class="w-9 h-9"
aria-label="Toggle dark mode" aria-label="Toggle dark mode"
:variant="'ghost'" :variant="'ghost'"
:size="'icon'" @click="toggleDark()" :size="'icon'"
@click="toggleDark()"
> >
<component <component
:is="isDark ? RadixIconsSun : RadixIconsMoon" :is="isDark ? RadixIconsSun : RadixIconsMoon"
class="w-[20px] h-5 text-foreground" class="w-5 h-5 text-foreground"
/> />
</Button> </Button>
</ClientOnly> </ClientOnly>
@ -292,4 +298,4 @@ watch(() => $route.path, (n) => {
<NewYorkSonner :theme="'system'" /> <NewYorkSonner :theme="'system'" />
<NewYorkToaster /> <NewYorkToaster />
</div> </div>
</template> </template>../components/CodeConfigCustomizer.vue

View File

@ -31,6 +31,7 @@
"embla-carousel-autoplay": "^8.0.0", "embla-carousel-autoplay": "^8.0.0",
"embla-carousel-vue": "^8.0.0", "embla-carousel-vue": "^8.0.0",
"lucide-vue-next": "^0.350.0", "lucide-vue-next": "^0.350.0",
"magic-string": "^0.30.8",
"radix-vue": "^1.5.2", "radix-vue": "^1.5.2",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"v-calendar": "^3.1.2", "v-calendar": "^3.1.2",

View File

@ -1,5 +1,5 @@
import { computed } from 'vue' import { computed } from 'vue'
import { useSessionStorage } from '@vueuse/core' import { useSessionStorage, useStorage } from '@vueuse/core'
import { useData } from 'vitepress' import { useData } from 'vitepress'
import { type Theme, themes } from './../lib/registry/themes' import { type Theme, themes } from './../lib/registry/themes'
import { type Style, styles } from '@/lib/registry/styles' import { type Style, styles } from '@/lib/registry/styles'
@ -10,6 +10,12 @@ interface Config {
style: Style style: Style
} }
interface CodeConfig {
prefix: string
componentsPath: string
utilsPath: string
}
export const RADII = [0, 0.25, 0.5, 0.75, 1] export const RADII = [0, 0.25, 0.5, 0.75, 1]
export function useConfigStore() { export function useConfigStore() {
@ -18,6 +24,11 @@ export function useConfigStore() {
radius: 0.5, radius: 0.5,
style: styles[0].name, style: styles[0].name,
}) })
const codeConfig = useStorage<CodeConfig>('code-config', {
prefix: '',
componentsPath: '@/components',
utilsPath: '@/utils',
})
const themeClass = computed(() => `theme-${config.value.theme}`) const themeClass = computed(() => `theme-${config.value.theme}`)
@ -40,5 +51,21 @@ export function useConfigStore() {
})` })`
}) })
return { config, theme, setTheme, radius, setRadius, themeClass, style, themePrimary } const setCodeConfig = (payload: CodeConfig) => {
codeConfig.value = payload
}
return {
config,
theme,
setTheme,
radius,
setRadius,
themeClass,
style,
themePrimary,
codeConfig,
setCodeConfig,
}
} }

View File

@ -89,6 +89,9 @@ importers:
lucide-vue-next: lucide-vue-next:
specifier: ^0.350.0 specifier: ^0.350.0
version: 0.350.0(vue@3.4.21) version: 0.350.0(vue@3.4.21)
magic-string:
specifier: ^0.30.8
version: 0.30.8
radix-vue: radix-vue:
specifier: ^1.5.2 specifier: ^1.5.2
version: 1.5.2(vue@3.4.21) version: 1.5.2(vue@3.4.21)