feat: create config sheet
This commit is contained in:
parent
c332ce1133
commit
e48c4dc2e9
|
|
@ -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>
|
||||||
|
|
@ -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, {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user