feat(module): devtools integration (#2196)

Co-authored-by: Benjamin Canac <canacb1@gmail.com>
This commit is contained in:
Romain Hamel
2024-11-05 22:17:56 +01:00
committed by GitHub
parent 7fc6b387b3
commit 701c75a2a8
100 changed files with 2062 additions and 59 deletions

View File

@@ -0,0 +1,71 @@
<script setup lang="ts">
import { onUnmounted, onMounted, reactive } from 'vue'
import { pascalCase } from 'scule'
import { defineAsyncComponent, useColorMode, useRoute } from '#imports'
const route = useRoute()
const component = route.query?.example
? defineAsyncComponent(() => import(`./examples/${route.query.example}.vue`))
: route.params?.slug && defineAsyncComponent(() => import(`../../runtime/components/${pascalCase(route.params.slug as string)}.vue`))
const state = reactive<{ slots?: any, props?: any }>({})
function onUpdateRenderer(event: Event & { data?: any }) {
state.props = { ...event.data.props }
state.slots = { ...event.data.slots }
}
const colorMode = useColorMode()
function setColorMode(event: Event & { isDark?: boolean }) {
colorMode.preference = event.isDark ? 'dark' : 'light'
}
onMounted(() => {
window.parent.addEventListener('nuxt-ui-devtools:update-renderer', onUpdateRenderer)
window.parent.addEventListener('nuxt-ui-devtools:set-color-mode', setColorMode)
})
onUnmounted(() => {
window.parent.removeEventListener('nuxt-ui-devtools:update-renderer', onUpdateRenderer)
window.parent.removeEventListener('nuxt-ui-devtools:set-color-mode', setColorMode)
})
onMounted(async () => {
const event: Event = new Event('nuxt-ui-devtools:component-loaded')
window.parent.dispatchEvent(event)
})
onMounted(() => {
if (!route.query?.example) return
})
</script>
<template>
<div id="ui-devtools-renderer" class="nuxt-ui-component-renderer">
<UApp :toaster="null">
<component :is="component" v-if="component" v-bind="state.props" :class="state?.slots?.base" :ui="state.slots" />
</UApp>
</div>
</template>
<style>
.nuxt-ui-component-renderer {
position: 'relative';
height: 100vh;
width: 100vw;
padding: 32px;
display: flex;
justify-content: center;
align-items: center;
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' transform='scale(3)'%3E%3Crect width='100%25' height='100%25' fill='%23fff'/%3E%3Cpath fill='none' stroke='hsla(0, 0%25, 98%25, 1)' stroke-width='.2' d='M10 0v20ZM0 10h20Z'/%3E%3C/svg%3E");
background-size: 40px 40px;
}
.dark .nuxt-ui-component-renderer {
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' transform='scale(3)'%3E%3Crect width='100%25' height='100%25' fill='hsl(0, 0%25, 8.5%25)'/%3E%3Cpath fill='none' stroke='hsl(0, 0%25, 11.0%25)' stroke-width='.2' d='M10 0v20ZM0 10h20Z'/%3E%3C/svg%3E");
background-size: 40px 40px;
}
</style>

View File

@@ -0,0 +1,7 @@
<template>
<UAvatarGroup>
<UAvatar src="https://github.com/benjamincanac.png" alt="Benjamin Canac" />
<UAvatar src="https://github.com/romhml.png" alt="Romain Hamel" />
<UAvatar src="https://github.com/noook.png" alt="Neil Richter" />
</UAvatarGroup>
</template>

View File

@@ -0,0 +1,8 @@
<template>
<UButtonGroup>
<UInput placeholder="Search..." />
<UButton color="neutral" variant="outline">
Button
</UButton>
</UButtonGroup>
</template>

View File

@@ -0,0 +1,13 @@
<template>
<div class="flex flex-col gap-4">
<UCard class="w-96">
<template #header>
<div class="bg-[var(--ui-bg-accented)]/40 h-8" />
</template>
<div class="bg-[var(--ui-bg-accented)]/40 h-32" />
<template #footer>
<div class="bg-[var(--ui-bg-accented)]/40 h-8" />
</template>
</UCard>
</div>
</template>

View File

@@ -0,0 +1,13 @@
<template>
<UCarousel
v-slot="{ item }"
class="basis-1/3"
:items="[
'https://picsum.photos/320/320?v=1',
'https://picsum.photos/320/320?v=2',
'https://picsum.photos/320/320?v=3'
]"
>
<img :src="item" class="rounded-lg basis-1/3">
</UCarousel>
</template>

View File

@@ -0,0 +1,5 @@
<template>
<UChip>
<UAvatar src="https://avatars.githubusercontent.com/u/739984?v=4" />
</UChip>
</template>

View File

@@ -0,0 +1,8 @@
<template>
<UCollapsible class="w-48">
<UButton label="Open Collapse" block />
<template #content>
<div class="bg-[var(--ui-bg-accented)]/40 h-60" />
</template>
</UCollapsible>
</template>

View File

@@ -0,0 +1,29 @@
<script setup lang="ts">
const groups = [{
id: 'actions',
items: [{
label: 'Add new file',
suffix: 'Create a new file in the current directory or workspace.',
icon: 'i-heroicons-document-plus'
}, {
label: 'Add new folder',
suffix: 'Create a new folder in the current directory or workspace.',
icon: 'i-heroicons-folder-plus',
kbds: ['meta', 'F']
}, {
label: 'Add hashtag',
suffix: 'Add a hashtag to the current item.',
icon: 'i-heroicons-hashtag',
kbds: ['meta', 'H']
}, {
label: 'Add label',
suffix: 'Add a label to the current item.',
icon: 'i-heroicons-tag',
kbds: ['meta', 'L']
}]
}]
</script>
<template>
<UCommandPalette :groups="groups" />
</template>

View File

@@ -0,0 +1,5 @@
<template>
<UContainer>
<div class="bg-[var(--ui-bg-accented)]/40 h-60 aspect-video w-72" />
</UContainer>
</template>

View File

@@ -0,0 +1,5 @@
<template>
<UContextMenu>
<div class="bg-[var(--ui-bg-accented)]/40 h-60 w-72" />
</UContextMenu>
</template>

View File

@@ -0,0 +1,8 @@
<template>
<UDrawer>
<UButton label="Open Drawer" />
<template #body>
<div class="size-96" />
</template>
</UDrawer>
</template>

View File

@@ -0,0 +1,5 @@
<template>
<UDropdownMenu>
<UButton label="Open Dropdown" />
</UDropdownMenu>
</template>

View File

@@ -0,0 +1,30 @@
<script setup lang="ts">
import { reactive } from 'vue'
const state = reactive({ email: undefined, password: undefined })
function validate(data: Partial<typeof state>) {
const errors: Array<{ name: string, message: string }> = []
if (!data.email) errors.push({ name: 'email', message: 'Required' })
if (!data.password) errors.push({ name: 'password', message: 'Required' })
return errors
}
</script>
<template>
<UForm
:validate="validate"
:state="state"
class="space-y-4"
>
<UFormField name="email" label="Email">
<UInput v-model="state.email" />
</UFormField>
<UFormField name="password" label="Password">
<UInput v-model="state.password" />
</UFormField>
<UButton type="submit">
Submit
</UButton>
</UForm>
</template>

View File

@@ -0,0 +1,5 @@
<template>
<UFormField>
<UInput />
</UFormField>
</template>

View File

@@ -0,0 +1,5 @@
<template>
<ULink>
Link
</ULink>
</template>

View File

@@ -0,0 +1,8 @@
<template>
<UModal>
<UButton label="Open Modal" />
<template #content>
<div class="h-72" />
</template>
</UModal>
</template>

View File

@@ -0,0 +1,8 @@
<template>
<UPopover>
<UButton label="Open Collapse" />
<template #content>
<div class="bg-[var(--ui-bg-accented)]/40 h-24 w-60" />
</template>
</UPopover>
</template>

View File

@@ -0,0 +1,3 @@
<template>
<USkeleton class="h-32 w-96" />
</template>

View File

@@ -0,0 +1,8 @@
<template>
<USlideover>
<UButton label="Open Slideover" />
<template #body>
<div class="size-96" />
</template>
</USlideover>
</template>

View File

@@ -0,0 +1,11 @@
<script setup>
import { useToast } from '#imports'
const toast = useToast()
</script>
<template>
<UToaster>
<UButton label="Open toast" @click="toast.add({ title: 'Heads up!' })" />
</UToaster>
</template>

View File

@@ -0,0 +1,5 @@
<template>
<UTooltip>
<div class="bg-[var(--ui-bg-accented)]/40 size-20" />
</UTooltip>
</template>