docs: fix block preview
This commit is contained in:
parent
96800c4c44
commit
64c0371280
237
apps/www/.vitepress/theme/components/BlockContainer.vue
Normal file
237
apps/www/.vitepress/theme/components/BlockContainer.vue
Normal file
|
|
@ -0,0 +1,237 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useConfigStore } from '@/stores/config'
|
||||||
|
import { CircleHelp, Info, Monitor, Smartphone, Tablet } from 'lucide-vue-next'
|
||||||
|
import MagicString from 'magic-string'
|
||||||
|
import { codeToHtml } from 'shiki'
|
||||||
|
import { reactive, ref, watch } from 'vue'
|
||||||
|
import { compileScript, parse, walk } from 'vue/compiler-sfc'
|
||||||
|
import { cssVariables } from '../config/shiki'
|
||||||
|
import BlockCopyButton from './BlockCopyButton.vue'
|
||||||
|
import StyleSwitcher from './StyleSwitcher.vue'
|
||||||
|
|
||||||
|
// import { V0Button } from '@/components/v0-button'
|
||||||
|
import { Badge } from '@/lib/registry/new-york/ui/badge'
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from '@/lib/registry/new-york/ui/popover'
|
||||||
|
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/lib/registry/new-york/ui/resizable'
|
||||||
|
import { Separator } from '@/lib/registry/new-york/ui/separator'
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/lib/registry/new-york/ui/tabs'
|
||||||
|
import { ToggleGroup, ToggleGroupItem } from '@/lib/registry/new-york/ui/toggle-group'
|
||||||
|
import BlockPreview from './BlockPreview.vue'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
name: string
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { style, codeConfig } = useConfigStore()
|
||||||
|
|
||||||
|
const isLoading = ref(true)
|
||||||
|
const tabValue = ref('preview')
|
||||||
|
const resizableRef = ref<InstanceType<typeof ResizablePanel>>()
|
||||||
|
|
||||||
|
const rawString = ref('')
|
||||||
|
const codeHtml = ref('')
|
||||||
|
const metadata = reactive({
|
||||||
|
description: null as string | null,
|
||||||
|
iframeHeight: null as string | null,
|
||||||
|
containerClass: null as string | null,
|
||||||
|
})
|
||||||
|
|
||||||
|
function removeScript(code: string) {
|
||||||
|
const s = new MagicString(code)
|
||||||
|
const scriptTagRegex = /<script\s+lang="ts"\s*>[\s\S]+?<\/script>/g
|
||||||
|
let match
|
||||||
|
// eslint-disable-next-line no-cond-assign
|
||||||
|
while ((match = scriptTagRegex.exec(code)) !== null) {
|
||||||
|
const start = match.index
|
||||||
|
const end = match.index + match[0].length
|
||||||
|
s.overwrite(start, end, '') // Replace the script tag with an empty string
|
||||||
|
}
|
||||||
|
return s.trimStart().toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformImportPath(code: string) {
|
||||||
|
const s = new MagicString(code)
|
||||||
|
s.replaceAll(`@/lib/registry/${style.value}`, codeConfig.value.componentsPath)
|
||||||
|
s.replaceAll(`@/lib/utils`, codeConfig.value.utilsPath)
|
||||||
|
return s.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
watch([style, codeConfig], async () => {
|
||||||
|
try {
|
||||||
|
const baseRawString = await import(`../../../src/lib/registry/${style.value}/block/${props.name}.vue?raw`).then(res => res.default.trim())
|
||||||
|
rawString.value = transformImportPath(removeScript(baseRawString))
|
||||||
|
|
||||||
|
if (!metadata.description) {
|
||||||
|
const { descriptor } = parse(baseRawString)
|
||||||
|
const ast = compileScript(descriptor, { id: '' })
|
||||||
|
walk(ast.scriptAst, {
|
||||||
|
enter(node: any) {
|
||||||
|
const declaration = node.declaration
|
||||||
|
// Check if the declaration is a variable declaration
|
||||||
|
if (declaration?.type === 'VariableDeclaration') {
|
||||||
|
// Extract variable names and their values
|
||||||
|
declaration.declarations.forEach((decl: any) => {
|
||||||
|
// @ts-expect-error ignore missing type
|
||||||
|
metadata[decl.id.name] = decl.init ? decl.init.value : null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
codeHtml.value = await codeToHtml(rawString.value, {
|
||||||
|
lang: 'vue',
|
||||||
|
theme: cssVariables,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
}, { immediate: true, deep: true })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Tabs
|
||||||
|
:id="name"
|
||||||
|
v-model="tabValue"
|
||||||
|
class="relative grid w-full scroll-m-20 gap-4"
|
||||||
|
:style=" {
|
||||||
|
'--container-height': metadata.iframeHeight ?? '600px',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col items-center gap-4 sm:flex-row">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<TabsList class="hidden sm:flex">
|
||||||
|
<TabsTrigger value="preview">
|
||||||
|
Preview
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger value="code">
|
||||||
|
Code
|
||||||
|
</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
<div class="hidden items-center gap-2 sm:flex">
|
||||||
|
<Separator
|
||||||
|
orientation="vertical"
|
||||||
|
class="mx-2 hidden h-4 md:flex"
|
||||||
|
/>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<a :href="`#${name}`">
|
||||||
|
<Badge variant="outline">{{ name }}</Badge>
|
||||||
|
</a>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger class="hidden text-muted-foreground hover:text-foreground sm:flex">
|
||||||
|
<Info class="h-3.5 w-3.5" />
|
||||||
|
<span class="sr-only">Block description</span>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent
|
||||||
|
side="right"
|
||||||
|
:side-offset="10"
|
||||||
|
class="text-sm"
|
||||||
|
>
|
||||||
|
{{ metadata.description }}
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2 pr-[14px] sm:ml-auto">
|
||||||
|
<div class="hidden h-[28px] items-center gap-1.5 rounded-md border p-[2px] shadow-sm md:flex">
|
||||||
|
<ToggleGroup
|
||||||
|
type="single"
|
||||||
|
default-value="100"
|
||||||
|
@update:model-value="(value) => {
|
||||||
|
resizableRef?.resize(parseInt(value as string))
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<ToggleGroupItem
|
||||||
|
value="100"
|
||||||
|
class="h-[22px] w-[22px] rounded-sm p-0"
|
||||||
|
>
|
||||||
|
<Monitor class="h-3.5 w-3.5" />
|
||||||
|
</ToggleGroupItem>
|
||||||
|
<ToggleGroupItem
|
||||||
|
value="60"
|
||||||
|
class="h-[22px] w-[22px] rounded-sm p-0"
|
||||||
|
>
|
||||||
|
<Tablet class="h-3.5 w-3.5" />
|
||||||
|
</ToggleGroupItem>
|
||||||
|
<ToggleGroupItem
|
||||||
|
value="30"
|
||||||
|
class="h-[22px] w-[22px] rounded-sm p-0"
|
||||||
|
>
|
||||||
|
<Smartphone class="h-3.5 w-3.5" />
|
||||||
|
</ToggleGroupItem>
|
||||||
|
</ToggleGroup>
|
||||||
|
</div>
|
||||||
|
<Separator
|
||||||
|
orientation="vertical"
|
||||||
|
class="mx-2 hidden h-4 md:flex"
|
||||||
|
/>
|
||||||
|
<StyleSwitcher class="h-7" />
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger class="hidden text-muted-foreground hover:text-foreground sm:flex">
|
||||||
|
<CircleHelp class="h-3.5 w-3.5" />
|
||||||
|
<span class="sr-only">Block description</span>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent
|
||||||
|
side="top"
|
||||||
|
:side-offset="20"
|
||||||
|
class="space-y-3 rounded-[0.5rem] text-sm"
|
||||||
|
>
|
||||||
|
<p class="font-medium">
|
||||||
|
What is the difference between the New York and Default style?
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
A style comes with its own set of components, animations,
|
||||||
|
icons and more.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The <span class="font-medium">Default</span> style has
|
||||||
|
larger inputs, uses lucide-vue-next for icons and
|
||||||
|
tailwindcss-animate for animations.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The <span class="font-medium">New York</span> style ships
|
||||||
|
with smaller buttons and inputs. It also uses shadows on cards
|
||||||
|
and buttons.
|
||||||
|
</p>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
<Separator orientation="vertical" class="mx-2 h-4" />
|
||||||
|
<BlockCopyButton :code="rawString" />
|
||||||
|
<!-- <V0Button
|
||||||
|
name="{block.name}"
|
||||||
|
description="{block.description" || "Edit in v0"}
|
||||||
|
code="{block.code}"
|
||||||
|
style="{block.style}"
|
||||||
|
/> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<TabsContent
|
||||||
|
v-show="tabValue === 'preview'"
|
||||||
|
force-mount
|
||||||
|
value="preview"
|
||||||
|
class="relative after:absolute after:inset-0 after:right-3 after:z-0 after:rounded-lg after:bg-muted h-[--container-height] px-0"
|
||||||
|
>
|
||||||
|
<ResizablePanelGroup id="block-resizable" direction="horizontal" class="relative z-10">
|
||||||
|
<ResizablePanel
|
||||||
|
id="block-resizable-panel-1"
|
||||||
|
ref="resizableRef"
|
||||||
|
:default-size="100"
|
||||||
|
:min-size="30"
|
||||||
|
:as-child="true"
|
||||||
|
>
|
||||||
|
<BlockPreview :name="name" styles="default" :container-class="metadata.containerClass ?? ''" container />
|
||||||
|
</ResizablePanel>
|
||||||
|
<ResizableHandle id="block-resizable-handle" class="relative hidden w-3 bg-transparent p-0 after:absolute after:right-0 after:top-1/2 after:h-8 after:w-[6px] after:-translate-y-1/2 after:translate-x-[-1px] after:rounded-full after:bg-border after:transition-all after:hover:h-10 sm:block" />
|
||||||
|
<ResizablePanel id="block-resizable-panel-2" :default-size="0" :min-size="0" />
|
||||||
|
</ResizablePanelGroup>
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent value="code" class="h-[--container-height]">
|
||||||
|
<div
|
||||||
|
class="language-vue !h-full !max-h-[none] !mt-0"
|
||||||
|
v-html="codeHtml"
|
||||||
|
/>
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
|
</template>
|
||||||
|
|
@ -2,11 +2,11 @@
|
||||||
import { useUrlSearchParams } from '@vueuse/core'
|
import { useUrlSearchParams } from '@vueuse/core'
|
||||||
import ComponentLoader from './ComponentLoader.vue'
|
import ComponentLoader from './ComponentLoader.vue'
|
||||||
|
|
||||||
const params = useUrlSearchParams('hash-params')
|
const params = useUrlSearchParams('history')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="params.name && params.style" :class="params.containerClass">
|
<div v-if="params.name" :class="params.containerClass">
|
||||||
<ComponentLoader :key="params.style?.toString()" :name="params.name?.toString()" :type-name="'block'" />
|
<ComponentLoader :key="params.style?.toString()" :name="params.name?.toString()" :type-name="'block'" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,245 +1,40 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useConfigStore } from '@/stores/config'
|
import { computed, ref } from 'vue'
|
||||||
import { CircleHelp, Info, Monitor, Smartphone, Tablet } from 'lucide-vue-next'
|
|
||||||
import MagicString from 'magic-string'
|
|
||||||
import { codeToHtml } from 'shiki'
|
|
||||||
import { reactive, ref, watch } from 'vue'
|
|
||||||
import { compileScript, parse, walk } from 'vue/compiler-sfc'
|
|
||||||
import { cssVariables } from '../config/shiki'
|
|
||||||
import BlockCopyButton from './BlockCopyButton.vue'
|
|
||||||
import Spinner from './Spinner.vue'
|
import Spinner from './Spinner.vue'
|
||||||
import StyleSwitcher from './StyleSwitcher.vue'
|
|
||||||
|
|
||||||
// import { V0Button } from '@/components/v0-button'
|
|
||||||
import { Badge } from '@/lib/registry/new-york/ui/badge'
|
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from '@/lib/registry/new-york/ui/popover'
|
|
||||||
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/lib/registry/new-york/ui/resizable'
|
|
||||||
import { Separator } from '@/lib/registry/new-york/ui/separator'
|
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/lib/registry/new-york/ui/tabs'
|
|
||||||
import { ToggleGroup, ToggleGroupItem } from '@/lib/registry/new-york/ui/toggle-group'
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
name: string
|
name: string
|
||||||
|
styles?: string
|
||||||
|
containerClass?: string
|
||||||
|
container?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const { style, codeConfig } = useConfigStore()
|
|
||||||
|
|
||||||
const isLoading = ref(true)
|
const isLoading = ref(true)
|
||||||
const tabValue = ref('preview')
|
|
||||||
const resizableRef = ref<InstanceType<typeof ResizablePanel>>()
|
|
||||||
|
|
||||||
const rawString = ref('')
|
const iframeURL = computed(() => {
|
||||||
const codeHtml = ref('')
|
const url = new URL(`${window.location.origin}/blocks/renderer`)
|
||||||
const metadata = reactive({
|
Object.entries(props).forEach(([key, value]) => {
|
||||||
description: null as string | null,
|
if (value)
|
||||||
iframeHeight: null as string | null,
|
url.searchParams.append(key, value as string)
|
||||||
containerClass: null as string | null,
|
|
||||||
})
|
})
|
||||||
|
return url.href
|
||||||
function removeScript(code: string) {
|
|
||||||
const s = new MagicString(code)
|
|
||||||
const scriptTagRegex = /<script\s+lang="ts"\s*>[\s\S]+?<\/script>/g
|
|
||||||
let match
|
|
||||||
// eslint-disable-next-line no-cond-assign
|
|
||||||
while ((match = scriptTagRegex.exec(code)) !== null) {
|
|
||||||
const start = match.index
|
|
||||||
const end = match.index + match[0].length
|
|
||||||
s.overwrite(start, end, '') // Replace the script tag with an empty string
|
|
||||||
}
|
|
||||||
return s.trimStart().toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
function transformImportPath(code: string) {
|
|
||||||
const s = new MagicString(code)
|
|
||||||
s.replaceAll(`@/lib/registry/${style.value}`, codeConfig.value.componentsPath)
|
|
||||||
s.replaceAll(`@/lib/utils`, codeConfig.value.utilsPath)
|
|
||||||
return s.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
watch([style, codeConfig], async () => {
|
|
||||||
try {
|
|
||||||
const baseRawString = await import(`../../../src/lib/registry/${style.value}/block/${props.name}.vue?raw`).then(res => res.default.trim())
|
|
||||||
rawString.value = transformImportPath(removeScript(baseRawString))
|
|
||||||
|
|
||||||
if (!metadata.description) {
|
|
||||||
const { descriptor } = parse(baseRawString)
|
|
||||||
const ast = compileScript(descriptor, { id: '' })
|
|
||||||
walk(ast.scriptAst, {
|
|
||||||
enter(node: any) {
|
|
||||||
const declaration = node.declaration
|
|
||||||
// Check if the declaration is a variable declaration
|
|
||||||
if (declaration?.type === 'VariableDeclaration') {
|
|
||||||
// Extract variable names and their values
|
|
||||||
declaration.declarations.forEach((decl: any) => {
|
|
||||||
// @ts-expect-error ignore missing type
|
|
||||||
metadata[decl.id.name] = decl.init ? decl.init.value : null
|
|
||||||
})
|
})
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
codeHtml.value = await codeToHtml(rawString.value, {
|
|
||||||
lang: 'vue',
|
|
||||||
theme: cssVariables,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
}
|
|
||||||
}, { immediate: true, deep: true })
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Tabs
|
<div class="relative rounded-lg border overflow-hidden bg-background" :class="[container ? '' : 'aspect-[4/2.5]']">
|
||||||
:id="name"
|
|
||||||
v-model="tabValue"
|
|
||||||
class="relative grid w-full scroll-m-20 gap-4"
|
|
||||||
:style=" {
|
|
||||||
'--container-height': metadata.iframeHeight ?? '600px',
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<div class="flex flex-col items-center gap-4 sm:flex-row">
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<TabsList class="hidden sm:flex">
|
|
||||||
<TabsTrigger value="preview">
|
|
||||||
Preview
|
|
||||||
</TabsTrigger>
|
|
||||||
<TabsTrigger value="code">
|
|
||||||
Code
|
|
||||||
</TabsTrigger>
|
|
||||||
</TabsList>
|
|
||||||
<div class="hidden items-center gap-2 sm:flex">
|
|
||||||
<Separator
|
|
||||||
orientation="vertical"
|
|
||||||
class="mx-2 hidden h-4 md:flex"
|
|
||||||
/>
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<a :href="`#${name}`">
|
|
||||||
<Badge variant="outline">{{ name }}</Badge>
|
|
||||||
</a>
|
|
||||||
<Popover>
|
|
||||||
<PopoverTrigger class="hidden text-muted-foreground hover:text-foreground sm:flex">
|
|
||||||
<Info class="h-3.5 w-3.5" />
|
|
||||||
<span class="sr-only">Block description</span>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent
|
|
||||||
side="right"
|
|
||||||
:side-offset="10"
|
|
||||||
class="text-sm"
|
|
||||||
>
|
|
||||||
{{ metadata.description }}
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-2 pr-[14px] sm:ml-auto">
|
|
||||||
<div class="hidden h-[28px] items-center gap-1.5 rounded-md border p-[2px] shadow-sm md:flex">
|
|
||||||
<ToggleGroup
|
|
||||||
type="single"
|
|
||||||
default-value="100"
|
|
||||||
@update:model-value="(value) => {
|
|
||||||
resizableRef?.resize(parseInt(value))
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<ToggleGroupItem
|
|
||||||
value="100"
|
|
||||||
class="h-[22px] w-[22px] rounded-sm p-0"
|
|
||||||
>
|
|
||||||
<Monitor class="h-3.5 w-3.5" />
|
|
||||||
</ToggleGroupItem>
|
|
||||||
<ToggleGroupItem
|
|
||||||
value="60"
|
|
||||||
class="h-[22px] w-[22px] rounded-sm p-0"
|
|
||||||
>
|
|
||||||
<Tablet class="h-3.5 w-3.5" />
|
|
||||||
</ToggleGroupItem>
|
|
||||||
<ToggleGroupItem
|
|
||||||
value="30"
|
|
||||||
class="h-[22px] w-[22px] rounded-sm p-0"
|
|
||||||
>
|
|
||||||
<Smartphone class="h-3.5 w-3.5" />
|
|
||||||
</ToggleGroupItem>
|
|
||||||
</ToggleGroup>
|
|
||||||
</div>
|
|
||||||
<Separator
|
|
||||||
orientation="vertical"
|
|
||||||
class="mx-2 hidden h-4 md:flex"
|
|
||||||
/>
|
|
||||||
<StyleSwitcher class="h-7" />
|
|
||||||
<Popover>
|
|
||||||
<PopoverTrigger class="hidden text-muted-foreground hover:text-foreground sm:flex">
|
|
||||||
<CircleHelp class="h-3.5 w-3.5" />
|
|
||||||
<span class="sr-only">Block description</span>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent
|
|
||||||
side="top"
|
|
||||||
:side-offset="20"
|
|
||||||
class="space-y-3 rounded-[0.5rem] text-sm"
|
|
||||||
>
|
|
||||||
<p class="font-medium">
|
|
||||||
What is the difference between the New York and Default style?
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
A style comes with its own set of components, animations,
|
|
||||||
icons and more.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
The <span class="font-medium">Default</span> style has
|
|
||||||
larger inputs, uses lucide-vue-next for icons and
|
|
||||||
tailwindcss-animate for animations.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
The <span class="font-medium">New York</span> style ships
|
|
||||||
with smaller buttons and inputs. It also uses shadows on cards
|
|
||||||
and buttons.
|
|
||||||
</p>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
<Separator orientation="vertical" class="mx-2 h-4" />
|
|
||||||
<BlockCopyButton :code="rawString" />
|
|
||||||
<!-- <V0Button
|
|
||||||
name="{block.name}"
|
|
||||||
description="{block.description" || "Edit in v0"}
|
|
||||||
code="{block.code}"
|
|
||||||
style="{block.style}"
|
|
||||||
/> -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<TabsContent
|
|
||||||
v-show="tabValue === 'preview'"
|
|
||||||
force-mount
|
|
||||||
value="preview"
|
|
||||||
class="relative after:absolute after:inset-0 after:right-3 after:z-0 after:rounded-lg after:bg-muted h-[--container-height] px-0"
|
|
||||||
>
|
|
||||||
<ResizablePanelGroup id="block-resizable" direction="horizontal" class="relative z-10">
|
|
||||||
<ResizablePanel
|
|
||||||
id="block-resizable-panel-1"
|
|
||||||
ref="resizableRef"
|
|
||||||
class="relative rounded-lg border bg-background transition-all "
|
|
||||||
:default-size="100"
|
|
||||||
:min-size="30"
|
|
||||||
>
|
|
||||||
<div v-if="isLoading" class="flex items-center justify-center h-full">
|
<div v-if="isLoading" class="flex items-center justify-center h-full">
|
||||||
<Spinner />
|
<Spinner />
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
:class="[container ? 'w-full' : 'absolute inset-0 hidden w-[1600px] bg-background md:block']"
|
||||||
|
>
|
||||||
<iframe
|
<iframe
|
||||||
v-show="!isLoading"
|
v-show="!isLoading"
|
||||||
:src="`/blocks/renderer#name=${name}&style=${style}&containerClass=${encodeURIComponent(metadata.containerClass ?? '')}`"
|
:src="iframeURL"
|
||||||
class="relative z-20 w-full bg-background h-[--container-height]"
|
class="relative z-20 w-full bg-background" :class="[container ? 'h-[--container-height]' : 'size-full']"
|
||||||
@load="isLoading = false"
|
@load="isLoading = false"
|
||||||
/>
|
/>
|
||||||
</ResizablePanel>
|
</div>
|
||||||
<ResizableHandle id="block-resizable-handle" class="relative hidden w-3 bg-transparent p-0 after:absolute after:right-0 after:top-1/2 after:h-8 after:w-[6px] after:-translate-y-1/2 after:translate-x-[-1px] after:rounded-full after:bg-border after:transition-all after:hover:h-10 sm:block" />
|
</div>
|
||||||
<ResizablePanel id="block-resizable-panel-2" :default-size="0" :min-size="0" />
|
|
||||||
</ResizablePanelGroup>
|
|
||||||
</TabsContent>
|
|
||||||
<TabsContent value="code" class="h-[--container-height]">
|
|
||||||
<div
|
|
||||||
class="language-vue !h-full !max-h-[none] !mt-0"
|
|
||||||
v-html="codeHtml"
|
|
||||||
/>
|
|
||||||
</TabsContent>
|
|
||||||
</Tabs>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import PageHeader from '../components/PageHeader.vue'
|
||||||
import PageHeaderDescription from '../components/PageHeaderDescription.vue'
|
import PageHeaderDescription from '../components/PageHeaderDescription.vue'
|
||||||
|
|
||||||
import PageHeaderHeading from '../components/PageHeaderHeading.vue'
|
import PageHeaderHeading from '../components/PageHeaderHeading.vue'
|
||||||
import BlockPreview from './BlockPreview.vue'
|
import BlockContainer from './BlockContainer.vue'
|
||||||
|
|
||||||
const blocks = ref<string[]>([])
|
const blocks = ref<string[]>([])
|
||||||
|
|
||||||
|
|
@ -48,6 +48,6 @@ import('../../../__registry__/index').then((res) => {
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<section id="blocks" class="grid scroll-mt-24 gap-24 lg:gap-48">
|
<section id="blocks" class="grid scroll-mt-24 gap-24 lg:gap-48">
|
||||||
<BlockPreview v-for="block in blocks" :key="block" :name="block" />
|
<BlockContainer v-for="block in blocks" :key="block" :name="block" />
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
export { default as APITable } from './APITable.vue'
|
export { default as APITable } from './APITable.vue'
|
||||||
|
export { default as BlockPreview } from './BlockPreview.vue'
|
||||||
export { default as Callout } from './Callout.vue'
|
export { default as Callout } from './Callout.vue'
|
||||||
export { default as CodeWrapper } from './CodeWrapper'
|
export { default as CodeWrapper } from './CodeWrapper'
|
||||||
export { default as ComponentPreview } from './ComponentPreview.vue'
|
export { default as ComponentPreview } from './ComponentPreview.vue'
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,16 @@
|
||||||
--ring: 240 5% 64.9%;
|
--ring: 240 5% 64.9%;
|
||||||
--radius: 0.5rem;
|
--radius: 0.5rem;
|
||||||
|
|
||||||
|
--sidebar-background: 0 0% 98%;
|
||||||
|
--sidebar-foreground: 240 5.3% 26.1%;
|
||||||
|
--sidebar-primary: 240 5.9% 10%;
|
||||||
|
--sidebar-primary-foreground: 0 0% 98%;
|
||||||
|
--sidebar-accent: 240 4.8% 95.9%;
|
||||||
|
--sidebar-accent-foreground: 240 5.9% 10%;
|
||||||
|
--sidebar-border: 220 13% 91%;
|
||||||
|
--sidebar-ring: 217.2 91.2% 59.8%;
|
||||||
|
|
||||||
|
|
||||||
--vis-primary-color: var(--primary);
|
--vis-primary-color: var(--primary);
|
||||||
--vis-secondary-color: 160 81% 40%;
|
--vis-secondary-color: 160 81% 40%;
|
||||||
--vis-text-color: var(--muted-foreground);
|
--vis-text-color: var(--muted-foreground);
|
||||||
|
|
@ -64,6 +74,15 @@
|
||||||
--border: 240 3.7% 15.9%;
|
--border: 240 3.7% 15.9%;
|
||||||
--input: 240 3.7% 15.9%;
|
--input: 240 3.7% 15.9%;
|
||||||
--ring: 240 4.9% 83.9%;
|
--ring: 240 4.9% 83.9%;
|
||||||
|
|
||||||
|
--sidebar-background: 240 5.9% 10%;
|
||||||
|
--sidebar-foreground: 240 4.8% 95.9%;
|
||||||
|
--sidebar-primary: 224.3 76.3% 48%;
|
||||||
|
--sidebar-primary-foreground: 0 0% 100%;
|
||||||
|
--sidebar-accent: 240 3.7% 15.9%;
|
||||||
|
--sidebar-accent-foreground: 240 4.8% 95.9%;
|
||||||
|
--sidebar-border: 240 3.7% 15.9%;
|
||||||
|
--sidebar-ring: 217.2 91.2% 59.8%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -97,19 +116,6 @@
|
||||||
src: url("/fonts/Geist/GeistVariableVF.woff2") format("woff2");
|
src: url("/fonts/Geist/GeistVariableVF.woff2") format("woff2");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* === Scrollbars === */
|
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
@apply w-2;
|
|
||||||
@apply h-2;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
|
||||||
@apply !bg-muted;
|
|
||||||
}
|
|
||||||
::-webkit-scrollbar-thumb {
|
|
||||||
@apply rounded-sm !bg-muted-foreground/30;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Firefox */
|
/* Firefox */
|
||||||
/* https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-color#browser_compatibility */
|
/* https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-color#browser_compatibility */
|
||||||
|
|
@ -121,15 +127,6 @@
|
||||||
scrollbar-color: hsl(215.4 16.3% 56.9% / 0.3);
|
scrollbar-color: hsl(215.4 16.3% 56.9% / 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.hide-scrollbar::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hide-scrollbar {
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
scrollbar-width: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.antialised {
|
.antialised {
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
|
|
||||||
|
|
@ -1501,6 +1501,20 @@ export const Index = {
|
||||||
component: () => import("../src/lib/registry/default/block/Dashboard07.vue").then((m) => m.default),
|
component: () => import("../src/lib/registry/default/block/Dashboard07.vue").then((m) => m.default),
|
||||||
files: ["../src/lib/registry/default/block/Dashboard07.vue"],
|
files: ["../src/lib/registry/default/block/Dashboard07.vue"],
|
||||||
},
|
},
|
||||||
|
"Sidebar01": {
|
||||||
|
name: "Sidebar01",
|
||||||
|
type: "components:block",
|
||||||
|
registryDependencies: ["breadcrumb","dropdown-menu","label","separator","sidebar"],
|
||||||
|
component: () => import("../src/lib/registry/default/block/Sidebar01.vue").then((m) => m.default),
|
||||||
|
files: ["../src/lib/registry/default/block/Sidebar01.vue"],
|
||||||
|
},
|
||||||
|
"Sidebar07": {
|
||||||
|
name: "Sidebar07",
|
||||||
|
type: "components:block",
|
||||||
|
registryDependencies: ["avatar","breadcrumb","collapsible","dropdown-menu","separator","sidebar"],
|
||||||
|
component: () => import("../src/lib/registry/default/block/Sidebar07.vue").then((m) => m.default),
|
||||||
|
files: ["../src/lib/registry/default/block/Sidebar07.vue"],
|
||||||
|
},
|
||||||
}, "new-york": {
|
}, "new-york": {
|
||||||
"AccordionDemo": {
|
"AccordionDemo": {
|
||||||
name: "AccordionDemo",
|
name: "AccordionDemo",
|
||||||
|
|
@ -3000,5 +3014,19 @@ export const Index = {
|
||||||
component: () => import("../src/lib/registry/new-york/block/Dashboard07.vue").then((m) => m.default),
|
component: () => import("../src/lib/registry/new-york/block/Dashboard07.vue").then((m) => m.default),
|
||||||
files: ["../src/lib/registry/new-york/block/Dashboard07.vue"],
|
files: ["../src/lib/registry/new-york/block/Dashboard07.vue"],
|
||||||
},
|
},
|
||||||
|
"Sidebar01": {
|
||||||
|
name: "Sidebar01",
|
||||||
|
type: "components:block",
|
||||||
|
registryDependencies: ["breadcrumb","dropdown-menu","label","separator","sidebar"],
|
||||||
|
component: () => import("../src/lib/registry/new-york/block/Sidebar01.vue").then((m) => m.default),
|
||||||
|
files: ["../src/lib/registry/new-york/block/Sidebar01.vue"],
|
||||||
|
},
|
||||||
|
"Sidebar07": {
|
||||||
|
name: "Sidebar07",
|
||||||
|
type: "components:block",
|
||||||
|
registryDependencies: ["avatar","breadcrumb","collapsible","dropdown-menu","separator","sidebar"],
|
||||||
|
component: () => import("../src/lib/registry/new-york/block/Sidebar07.vue").then((m) => m.default),
|
||||||
|
files: ["../src/lib/registry/new-york/block/Sidebar07.vue"],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,10 +80,17 @@ for (const style of styles) {
|
||||||
continue
|
continue
|
||||||
|
|
||||||
const files = item.files?.map((file) => {
|
const files = item.files?.map((file) => {
|
||||||
let content = fs.readFileSync(
|
let content: string = ''
|
||||||
|
|
||||||
|
try {
|
||||||
|
content = fs.readFileSync(
|
||||||
path.join(process.cwd(), 'src/lib/registry', style.name, file),
|
path.join(process.cwd(), 'src/lib/registry', style.name, file),
|
||||||
'utf8',
|
'utf8',
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.log(`'${file}' is missing`)
|
||||||
|
}
|
||||||
|
|
||||||
// Replace Windows-style newlines with Unix-style newlines
|
// Replace Windows-style newlines with Unix-style newlines
|
||||||
content = content.replace(/\r\n/g, newLine)
|
content = content.replace(/\r\n/g, newLine)
|
||||||
|
|
@ -99,7 +106,7 @@ for (const style of styles) {
|
||||||
files,
|
files,
|
||||||
}
|
}
|
||||||
|
|
||||||
const payloadStr = JSON.stringify(payload, null, 2).replace(/\r\n/g, newLine)
|
const payloadStr = `${JSON.stringify(payload, null, 2).replace(/\r\n/g, newLine)}\n`
|
||||||
|
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.join(targetPath, `${item.name}.json`),
|
path.join(targetPath, `${item.name}.json`),
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,16 @@ export default {
|
||||||
DEFAULT: 'hsl(var(--card))',
|
DEFAULT: 'hsl(var(--card))',
|
||||||
foreground: 'hsl(var(--card-foreground))',
|
foreground: 'hsl(var(--card-foreground))',
|
||||||
},
|
},
|
||||||
|
sidebar: {
|
||||||
|
'DEFAULT': 'hsl(var(--sidebar-background))',
|
||||||
|
'foreground': 'hsl(var(--sidebar-foreground))',
|
||||||
|
'primary': 'hsl(var(--sidebar-primary))',
|
||||||
|
'primary-foreground': 'hsl(var(--sidebar-primary-foreground))',
|
||||||
|
'accent': 'hsl(var(--sidebar-accent))',
|
||||||
|
'accent-foreground': 'hsl(var(--sidebar-accent-foreground))',
|
||||||
|
'border': 'hsl(var(--sidebar-border))',
|
||||||
|
'ring': 'hsl(var(--sidebar-ring))',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
borderRadius: {
|
borderRadius: {
|
||||||
xl: 'calc(var(--radius) + 4px)',
|
xl: 'calc(var(--radius) + 4px)',
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user