feat(module): add support for vue using unplugin (#2416)

Co-authored-by: Benjamin Canac <canacb1@gmail.com>
This commit is contained in:
Daniel Roe
2024-10-25 16:15:26 +01:00
committed by GitHub
parent 50c6bf0092
commit d4a943e631
97 changed files with 18948 additions and 117 deletions

12
playground-vue/index.html Normal file
View File

@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Nuxt UI ❤️ Vue</title>
</head>
<body>
<div id="app" class="isolate"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@@ -0,0 +1,24 @@
{
"name": "playground-vue",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": {
"@nuxt/ui": "latest",
"vue": "^3.5.10",
"vue-router": "^4.4.5"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.1.4",
"typescript": "^5.5.3",
"unplugin-auto-import": "^0.18.3",
"unplugin-vue-components": "^0.27.4",
"vite": "^5.4.8",
"vue-tsc": "^2.1.6"
}
}

120
playground-vue/src/app.vue Normal file
View File

@@ -0,0 +1,120 @@
<script setup lang="ts">
import { splitByCase, upperFirst } from 'scule'
import { useRouter } from 'vue-router'
import { reactive, ref } from 'vue'
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore included for compatibility with Nuxt playground
import { useAppConfig } from '#imports'
const appConfig = useAppConfig()
appConfig.toaster = reactive({
position: 'bottom-right' as const,
expand: true,
duration: 5000
})
const router = useRouter()
const components = [
'accordion',
'alert',
'avatar',
'badge',
'breadcrumb',
'button',
'button-group',
'card',
'carousel',
'checkbox',
'chip',
'collapsible',
'context-menu',
'command-palette',
'drawer',
'dropdown-menu',
'form',
'form-field',
'input',
'input-menu',
'kbd',
'link',
'modal',
'navigation-menu',
'pagination',
'popover',
'progress',
'radio-group',
'select',
'select-menu',
'separator',
'shortcuts',
'skeleton',
'slideover',
'slider',
'switch',
'tabs',
'textarea',
'toast',
'tooltip'
]
const items = components.map(component => ({ label: upperName(component), to: `/components/${component}` }))
function upperName(name: string) {
return splitByCase(name).map(p => upperFirst(p)).join('')
}
const isCommandPaletteOpen = ref(false)
function onSelect(item: any) {
router.push(item.to)
}
defineShortcuts({
meta_k: () => isCommandPaletteOpen.value = true
})
</script>
<template>
<UApp :toaster="(appConfig.toaster as any)">
<div class="h-screen w-screen overflow-hidden flex min-h-0 bg-[var(--ui-bg)]" vaul-drawer-wrapper>
<UNavigationMenu :items="items" orientation="vertical" class="hidden lg:flex border-r border-[var(--ui-border)] overflow-y-auto w-48 p-4" />
<UNavigationMenu :items="items" orientation="horizontal" class="lg:hidden border-b border-[var(--ui-border)] overflow-x-auto" />
<div class="flex-1 flex flex-col items-center justify-around overflow-y-auto w-full py-12 px-4">
<Suspense>
<RouterView />
</Suspense>
</div>
</div>
<UModal v-model:open="isCommandPaletteOpen" class="sm:h-96">
<template #content>
<UCommandPalette placeholder="Search a component..." :groups="[{ id: 'items', items }]" :fuse="{ resultLimit: 100 }" @update:model-value="onSelect" @update:open="value => isCommandPaletteOpen = value" />
</template>
</UModal>
</UApp>
</template>
<style>
@import "tailwindcss";
@import "@nuxt/ui";
@theme {
--font-family-sans: 'Public Sans', sans-serif;
--color-green-50: #EFFDF5;
--color-green-100: #D9FBE8;
--color-green-200: #B3F5D1;
--color-green-300: #75EDAE;
--color-green-400: #00DC82;
--color-green-500: #00C16A;
--color-green-600: #00A155;
--color-green-700: #007F45;
--color-green-800: #016538;
--color-green-900: #0A5331;
--color-green-950: #052E16;
}
</style>

View File

@@ -0,0 +1,54 @@
import { createApp, ref } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import uiPlugin from '@nuxt/ui/vue-plugin'
import App from './app.vue'
const pages = import.meta.glob('../../playground/app/pages/**/*.vue')
const components = import.meta.glob('../../playground/app/components/**/*.vue')
const routes = Object.keys(pages).map((path) => {
const name = path.match(/\.\.\/\.\.\/playground\/app\/pages(.*)\.vue$/)![1].toLowerCase()
return {
path: name === '/index' ? '/' : name,
component: pages[path]
}
})
const router = createRouter({
routes,
history: createWebHistory()
})
const app = createApp(App)
Object.entries(components).forEach(([path, component]) => {
const name = path.split('/').pop()!.replace('.vue', '')
app.component(name, defineAsyncComponent(component as any))
})
app.use(router)
app.use(uiPlugin)
// @ts-expect-error unknown global property
globalThis.useFetch = async (url: string, options: RequestInit & { transform?: (data) => any } = {}) => {
const data = ref()
const status = ref('idle')
async function _fetch() {
status.value = 'loading'
try {
data.value = await fetch(url, options).then(r => r.json()).then(r => options.transform ? options.transform(r) : r)
status.value = 'success'
} catch (error) {
console.error(error)
status.value = 'error'
}
}
_fetch()
return Promise.resolve({
data,
status
})
}
app.mount('#app')

1
playground-vue/src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />

View File

@@ -0,0 +1,33 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": [
"ES2020",
"DOM",
"DOM.Iterable"
],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "preserve",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"auto-imports.d.ts",
"components.d.ts",
"app.config.ts"
]
}

View File

@@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

View File

@@ -0,0 +1,22 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["vite.config.ts"]
}

View File

@@ -0,0 +1,35 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImports from 'unplugin-auto-import/vite'
import VueComponents from 'unplugin-vue-components'
import ui from '../src/vite'
// https://vitejs.dev/config/
export default defineConfig({
server: {
fs: {
allow: ['..']
}
},
plugins: [
vue(),
ui({
ui: {
colors: {
primary: 'green',
neutral: 'slate'
}
}
}),
// these are required as we share the component pages with the Nuxt playground
AutoImports({ imports: ['vue'] }),
VueComponents.vite({
dirs: ['../playground/app/components']
})
],
optimizeDeps: {
// prevents reloading page when navigating between components
include: ['radix-vue/namespaced', 'vaul-vue', 'fast-deep-equal', 'embla-carousel-vue', 'embla-carousel-autoplay', 'embla-carousel-auto-scroll', 'embla-carousel-auto-height', 'embla-carousel-class-names', 'embla-carousel-fade', 'embla-carousel-wheel-gestures']
}
})