mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-22 16:00:39 +01:00
feat: rewrite to use app config and rework docs (#143)
Co-authored-by: Daniel Roe <daniel@roe.dev> Co-authored-by: Sébastien Chopin <seb@nuxt.com>
This commit is contained in:
@@ -1,509 +0,0 @@
|
||||
<!-- eslint-disable vue/no-template-shadow -->
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<!-- eslint-disable vue/no-v-text-v-html-on-component -->
|
||||
<template>
|
||||
<UCard v-if="component" class="relative flex flex-col" body-class="px-4 py-5 sm:p-6 relative" footer-class="flex flex-col flex-1 overflow-hidden">
|
||||
<div class="flex justify-center">
|
||||
<component :is="`U${defaultProps[params.component].component.name}`" v-if="defaultProps[params.component] && defaultProps[params.component].component" v-bind="defaultProps[params.component].component.props" />
|
||||
|
||||
<component :is="is" v-bind="{ ...boundProps, ...eventProps }">
|
||||
<template v-for="[key, slot] of Object.entries(defaultProps[params.component]?.slots || {}) || []" #[key]>
|
||||
<template v-if="Array.isArray(slot)">
|
||||
<div :key="key">
|
||||
<component
|
||||
:is="slot.component ? `U${slot.component.name}` : slot.tag"
|
||||
v-for="(slot, index) of slot"
|
||||
:key="index"
|
||||
:class="slot.class"
|
||||
v-bind="slot.component?.props || defaultProps[slot.component]"
|
||||
v-html="slot.html"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<component :is="`U${slot.component.name}`" v-if="slot.component" :key="`${key}-component`" v-bind="slot.component?.props || defaultProps[slot.component]" />
|
||||
<component :is="slot.tag" v-else :key="`${key}-tag`" :class="slot.class" v-html="slot.html" />
|
||||
</template>
|
||||
</template>
|
||||
</component>
|
||||
</div>
|
||||
|
||||
<template v-if="props.length" #footer>
|
||||
<div class="border-b u-border-gray-200">
|
||||
<pre class="text-sm leading-6 u-text-gray-900 flex-1 relative flex ligatures-none overflow-x-hidden px-4 sm:px-6 py-5 sm:py-6">
|
||||
<code class="flex-none min-w-full whitespace-pre-wrap break-all">{{ code }}</code>
|
||||
|
||||
<UButton
|
||||
class="absolute top-0 right-0"
|
||||
:icon="copied ? 'i-heroicons-clipboard-document-check' : 'i-heroicons-clipboard-document'"
|
||||
variant="transparent"
|
||||
size="sm"
|
||||
:custom-class="copied ? '!text-green-500' : ''"
|
||||
@click="onCopy"
|
||||
/>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 space-y-3">
|
||||
<UFormGroup
|
||||
v-for="prop of props"
|
||||
:key="prop.key"
|
||||
class="capitalize"
|
||||
:name="prop.key"
|
||||
:label="prop.key"
|
||||
>
|
||||
<UToggle
|
||||
v-if="prop.type === 'Boolean'"
|
||||
v-model="prop.value"
|
||||
:name="prop.key"
|
||||
/>
|
||||
<USelect
|
||||
v-else-if="prop.values"
|
||||
v-model="prop.value"
|
||||
:name="prop.key"
|
||||
placeholder="Choose one..."
|
||||
:options="prop.values"
|
||||
size="sm"
|
||||
/>
|
||||
<UInput
|
||||
v-else-if="prop.type === 'String'"
|
||||
v-model="prop.value"
|
||||
:name="prop.key"
|
||||
size="sm"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<UInput
|
||||
v-else-if="prop.type === 'Number'"
|
||||
v-model="prop.value"
|
||||
type="number"
|
||||
:name="prop.key"
|
||||
size="sm"
|
||||
/>
|
||||
<UTextarea
|
||||
v-else
|
||||
:model-value="prop.value && JSON.stringify(prop.value)"
|
||||
:name="prop.key"
|
||||
size="sm"
|
||||
:rows="8"
|
||||
autoresize
|
||||
@update:model-value="value => prop.value = JSON.parse(value)"
|
||||
/>
|
||||
</UFormGroup>
|
||||
</div>
|
||||
</template>
|
||||
</UCard>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useClipboard } from '@vueuse/core'
|
||||
import $ui from '#build/ui'
|
||||
|
||||
const nuxtApp = useNuxtApp()
|
||||
const { params } = useRoute()
|
||||
|
||||
const is = `U${params.component}`
|
||||
|
||||
const component = nuxtApp.vueApp.component(is)
|
||||
|
||||
const people = [
|
||||
{ id: 1, label: 'Durward Reynolds', disabled: false },
|
||||
{ id: 2, label: 'Kenton Towne', disabled: false },
|
||||
{ id: 3, label: 'Therese Wunsch', disabled: false },
|
||||
{ id: 4, label: 'Benedict Kessler', disabled: true },
|
||||
{ id: 5, label: 'Katelyn Rohan', disabled: false }
|
||||
]
|
||||
|
||||
const selectCustom = ref(people[0])
|
||||
const commandPalette = ref(people[0])
|
||||
const alertDialog = ref(false)
|
||||
const toggle = ref(false)
|
||||
const modal = ref(false)
|
||||
const slideover = ref(false)
|
||||
|
||||
const x = ref(0)
|
||||
const y = ref(0)
|
||||
const isContextMenuOpen = ref(false)
|
||||
const virtualElement = ref({ getBoundingClientRect: () => ({}) })
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('mousemove', ({ clientX, clientY }) => {
|
||||
x.value = clientX
|
||||
y.value = clientY
|
||||
})
|
||||
})
|
||||
|
||||
function openContextMenu () {
|
||||
const top = unref(y)
|
||||
const left = unref(x)
|
||||
|
||||
virtualElement.value.getBoundingClientRect = () => ({
|
||||
width: 0,
|
||||
height: 0,
|
||||
top,
|
||||
left
|
||||
})
|
||||
isContextMenuOpen.value = true
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
Button: {
|
||||
label: 'Button text'
|
||||
},
|
||||
Badge: {
|
||||
label: 'Badge'
|
||||
},
|
||||
Alert: {
|
||||
title: 'A new software update is available. See what’s new in version 2.0.4.'
|
||||
},
|
||||
AlertDialog: {
|
||||
icon: 'i-heroicons-exclamation-triangle',
|
||||
title: 'Deactivate account',
|
||||
description: 'Are you sure you want to deactivate your account? All of your data will be permanently removed from our servers forever. This action cannot be undone.',
|
||||
modelValue: alertDialog,
|
||||
'onUpdate:modelValue': (v) => { alertDialog.value = v },
|
||||
component: {
|
||||
name: 'Button',
|
||||
props: {
|
||||
variant: 'secondary',
|
||||
label: 'Open modal',
|
||||
onClick: () => { alertDialog.value = true }
|
||||
}
|
||||
}
|
||||
},
|
||||
Avatar: {
|
||||
src: 'https://picsum.photos/200/300'
|
||||
},
|
||||
AvatarGroup: {
|
||||
group: ['https://images.unsplash.com/photo-1491528323818-fdd1faba62cc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80', 'https://images.unsplash.com/photo-1550525811-e5869dd03032?ixlib=rb-1.2.1&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80', 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2.25&w=256&h=256&q=80', 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80']
|
||||
},
|
||||
Dropdown: {
|
||||
items: [
|
||||
[{
|
||||
label: 'Edit',
|
||||
icon: 'i-heroicons-pencil-square-20-solid'
|
||||
}, {
|
||||
label: 'Duplicate',
|
||||
icon: 'i-heroicons-document-duplicate-20-solid'
|
||||
}],
|
||||
[{
|
||||
label: 'Archive',
|
||||
icon: 'i-heroicons-archive-box-20-solid'
|
||||
}, {
|
||||
label: 'Move',
|
||||
icon: 'i-heroicons-arrow-right-circle-20-solid'
|
||||
}],
|
||||
[{
|
||||
label: 'Delete',
|
||||
icon: 'i-heroicons-trash-20-solid'
|
||||
}]
|
||||
]
|
||||
},
|
||||
VerticalNavigation: {
|
||||
links: [
|
||||
{
|
||||
label: 'Home',
|
||||
icon: 'i-heroicons-home',
|
||||
to: '/'
|
||||
},
|
||||
{
|
||||
label: 'Examples',
|
||||
icon: 'i-heroicons-book-open',
|
||||
to: '/examples'
|
||||
},
|
||||
{
|
||||
label: 'Migration',
|
||||
icon: 'i-heroicons-arrow-path',
|
||||
to: '/migration'
|
||||
},
|
||||
{
|
||||
label: 'External link',
|
||||
icon: 'i-heroicons-link',
|
||||
to: 'https://google.fr',
|
||||
target: '_blank'
|
||||
}
|
||||
]
|
||||
},
|
||||
CommandPalette: {
|
||||
modelValue: commandPalette,
|
||||
'onUpdate:modelValue': (v) => { commandPalette.value = v },
|
||||
groups: [{
|
||||
key: 'people',
|
||||
label: 'People',
|
||||
commands: people
|
||||
}]
|
||||
},
|
||||
Icon: {
|
||||
name: 'i-heroicons-bell'
|
||||
},
|
||||
Input: {
|
||||
name: 'input',
|
||||
placeholder: 'Enter text'
|
||||
},
|
||||
FormGroup: {
|
||||
name: 'input',
|
||||
label: 'Input group',
|
||||
slots: {
|
||||
default: {
|
||||
component: {
|
||||
name: 'Input',
|
||||
props: {
|
||||
name: 'input',
|
||||
placeholder: 'Works with every form element'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Toggle: {
|
||||
modelValue: toggle,
|
||||
'onUpdate:modelValue': (v) => { toggle.value = v }
|
||||
},
|
||||
Checkbox: {
|
||||
name: 'checkbox'
|
||||
},
|
||||
Radio: {
|
||||
name: 'radio'
|
||||
},
|
||||
Select: {
|
||||
name: 'select',
|
||||
options: ['English', 'Spanish', 'French', 'German', 'Chinese']
|
||||
},
|
||||
SelectCustom: {
|
||||
modelValue: selectCustom,
|
||||
'onUpdate:modelValue': (v) => { selectCustom.value = v },
|
||||
textAttribute: 'label',
|
||||
options: people
|
||||
},
|
||||
Textarea: {
|
||||
name: 'textarea'
|
||||
},
|
||||
Tooltip: {
|
||||
text: 'Tooltip text'
|
||||
},
|
||||
Notification: {
|
||||
id: '1',
|
||||
title: 'Notification title',
|
||||
callback: 'console.log(\'Timer expired\')'
|
||||
},
|
||||
ContextMenu: {
|
||||
modelValue: isContextMenuOpen,
|
||||
'onUpdate:modelValue': (v) => { isContextMenuOpen.value = v },
|
||||
virtualElement,
|
||||
component: {
|
||||
name: 'Card',
|
||||
props: {
|
||||
variant: 'secondary',
|
||||
label: 'Open context menu',
|
||||
onClick: () => { isContextMenuOpen.value = false },
|
||||
onContextmenu: (e) => {
|
||||
e?.preventDefault()
|
||||
openContextMenu()
|
||||
},
|
||||
class: 'relative w-[300px] h-[100px]'
|
||||
}
|
||||
},
|
||||
slots: {
|
||||
default: {
|
||||
tag: 'div',
|
||||
html: 'Context menu content',
|
||||
class: 'rounded border u-border-gray-200 p-2'
|
||||
}
|
||||
}
|
||||
},
|
||||
Modal: {
|
||||
modelValue: modal,
|
||||
'onUpdate:modelValue': (v) => { modal.value = v },
|
||||
component: {
|
||||
name: 'Button',
|
||||
props: {
|
||||
variant: 'secondary',
|
||||
label: 'Open modal',
|
||||
onClick: () => { modal.value = true }
|
||||
}
|
||||
},
|
||||
slots: {
|
||||
default: {
|
||||
tag: 'div',
|
||||
html: 'Modal content'
|
||||
},
|
||||
footer: {
|
||||
component: {
|
||||
name: 'Button',
|
||||
props: {
|
||||
label: 'Close',
|
||||
onClick: () => { modal.value = false }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Slideover: {
|
||||
modelValue: slideover,
|
||||
'onUpdate:modelValue': (v) => { slideover.value = v },
|
||||
component: {
|
||||
name: 'Button',
|
||||
props: {
|
||||
variant: 'secondary',
|
||||
label: 'Open slideover',
|
||||
onClick: () => { slideover.value = true }
|
||||
}
|
||||
},
|
||||
slots: {
|
||||
default: {
|
||||
tag: 'div',
|
||||
html: 'Slideover content'
|
||||
}
|
||||
}
|
||||
},
|
||||
Popover: {
|
||||
slots: {
|
||||
panel: {
|
||||
tag: 'div',
|
||||
class: 'u-bg-gray-100 rounded-lg shadow-lg ring-1 ring-black ring-opacity-5 p-6',
|
||||
html: 'Popover content'
|
||||
}
|
||||
}
|
||||
},
|
||||
Tabs: {
|
||||
links: [{
|
||||
label: 'Usage',
|
||||
to: '/',
|
||||
exact: true
|
||||
}, {
|
||||
label: 'Examples',
|
||||
to: '/examples'
|
||||
}, {
|
||||
label: 'Migration',
|
||||
to: '/migration'
|
||||
}, {
|
||||
label: 'Tabs',
|
||||
to: '/components/Tabs'
|
||||
}]
|
||||
},
|
||||
Pills: {
|
||||
links: [{
|
||||
label: 'Usage',
|
||||
to: '/',
|
||||
exact: true
|
||||
}, {
|
||||
label: 'Examples',
|
||||
to: '/examples'
|
||||
}, {
|
||||
label: 'Migration',
|
||||
to: '/migration'
|
||||
}, {
|
||||
label: 'Pills',
|
||||
to: '/components/Pills'
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
const componentDefaultProps = defaultProps[params.component] || {}
|
||||
const { props: componentProps } = await component.__asyncLoader()
|
||||
|
||||
function lowercaseFirstLetter (string) {
|
||||
return string.charAt(0).toLowerCase() + string.slice(1)
|
||||
}
|
||||
|
||||
const refProps = Object.entries(componentProps).map(([key, prop]) => {
|
||||
const defaultValue = componentDefaultProps[key]
|
||||
const propDefault = (typeof prop.default === 'function' ? prop.default() : prop.default)
|
||||
let value = defaultValue !== undefined ? defaultValue : propDefault
|
||||
let type = prop.type
|
||||
if (Array.isArray(type)) {
|
||||
type = type[0].name
|
||||
} else {
|
||||
type = type.name
|
||||
}
|
||||
|
||||
let values
|
||||
if (prop.validator) {
|
||||
const arrayRegex = prop.validator.toString().match(/\[.*\]/g, '')
|
||||
if (arrayRegex) {
|
||||
values = JSON.parse(arrayRegex[0].replace(/'/g, '"')).filter(Boolean)
|
||||
} else {
|
||||
const $uiProp = $ui[lowercaseFirstLetter(params.component)][key]
|
||||
if ($uiProp) {
|
||||
values = Object.keys($uiProp).filter(Boolean)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (value) {
|
||||
if (type === 'String' && typeof value === 'string') {
|
||||
value = value.replace(/^'(.*)'$/, '$1')
|
||||
} else if (type === 'Array') {
|
||||
value = JSON.stringify(value)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
key,
|
||||
type,
|
||||
value,
|
||||
values,
|
||||
default: propDefault
|
||||
}
|
||||
})
|
||||
|
||||
const eventProps = Object.entries(componentDefaultProps)
|
||||
.filter(([key]) => !refProps.find(prop => prop.key === key))
|
||||
.filter(([key]) => !['slots'].includes(key))
|
||||
.reduce((acc, [key, value]) => {
|
||||
acc[key] = value
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
const props = ref(refProps)
|
||||
const boundProps = computed(() => {
|
||||
const bound = {}
|
||||
for (const prop of props.value) {
|
||||
let value = prop.value
|
||||
if (value === null) {
|
||||
continue
|
||||
}
|
||||
|
||||
try {
|
||||
if (prop.type === 'Array') {
|
||||
value = JSON.parse(value)
|
||||
} else if (prop.type === 'Number') {
|
||||
value = Number(value)
|
||||
} else if (prop.type === 'Function') {
|
||||
// eslint-disable-next-line no-new-func
|
||||
value = Function(value)
|
||||
}
|
||||
|
||||
bound[prop.key] = value
|
||||
} catch (e) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
return bound
|
||||
})
|
||||
|
||||
function toKebabCase (str) {
|
||||
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()
|
||||
}
|
||||
|
||||
const { copy, copied } = useClipboard({ copiedDuring: 2000 })
|
||||
const onCopy = () => {
|
||||
copy(code.value)
|
||||
}
|
||||
|
||||
const code = computed(() => {
|
||||
let code = `<U${params.component}`
|
||||
for (const prop of props.value) {
|
||||
if (prop.value === null) {
|
||||
continue
|
||||
}
|
||||
if (prop.value === prop.default) {
|
||||
continue
|
||||
}
|
||||
|
||||
const key = toKebabCase(prop.key)
|
||||
code += `\n ${prop.type === 'Boolean' ? ':' : ''}${key === 'model-value' ? 'v-model' : key}="${prop.value}"`
|
||||
}
|
||||
code += '\n/>'
|
||||
return code
|
||||
})
|
||||
</script>
|
||||
@@ -1,65 +0,0 @@
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<div class="pb-10 border-b u-border-gray-200 mb-10">
|
||||
<div>
|
||||
<h1 class="inline-block text-3xl font-extrabold u-text-gray-900 tracking-tight">
|
||||
Dark mode
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<p class="mt-1 text-lg u-text-gray-500">
|
||||
Dark mode implementation with <a href="https://color-mode.nuxtjs.org/" target="_blank" class="underline">Color Mode</a> module.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h2 class="font-bold text-2xl u-text-gray-900">
|
||||
Usage
|
||||
</h2>
|
||||
|
||||
<p>TailwindCSS takes advantage of the `dark` class on the html tag:</p>
|
||||
|
||||
<pre class="u-bg-gray-900 rounded-md u-text-white px-4">
|
||||
<code class="text-sm">
|
||||
{{ code4 }}
|
||||
</code>
|
||||
</pre>
|
||||
|
||||
<p>The `@nuxtjs/color-mode` module is now installed by default, you can easily implement a toggle button:</p>
|
||||
|
||||
<pre class="u-bg-gray-900 rounded-md u-text-white px-4">
|
||||
<code class="text-sm">
|
||||
{{ code1 }}
|
||||
</code>
|
||||
</pre>
|
||||
|
||||
<h2 class="font-bold text-2xl u-text-gray-900">
|
||||
Shortcuts
|
||||
</h2>
|
||||
|
||||
<p>A number of shortcuts are available to make your life with colors easier.</p>
|
||||
|
||||
<p>For each color utilities: `bg`, `text`, `border`, `ring` and `divide`, shortcuts for `white`, `black` and `gray` colors are generated (based on your prefix `u` by default) that handles the dark mode automatically:</p>
|
||||
|
||||
<pre class="u-bg-gray-900 rounded-md u-text-white px-4">
|
||||
<code class="text-sm">
|
||||
{{ code3 }}
|
||||
</code>
|
||||
</pre>
|
||||
|
||||
<p>For example `u-bg-gray-100` is a shortcut for `bg-gray-100 dark:bg-gray-800`. Take a look at the <a href="https://github.com/nuxtlabs/ui/blob/dev/src/index.ts#L61" target="_blank" class="underline">shortcuts definitions</a>.</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const code1 = `
|
||||
const colorMode = useColorMode()
|
||||
const toggleDark = () => {
|
||||
colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'
|
||||
}`
|
||||
const code3 = `
|
||||
<div class="u-bg-gray-100 border u-border-gray-200 u-text-gray-700"></div>
|
||||
`
|
||||
const code4 = `
|
||||
<div class="bg-white dark:bg-black"></div>
|
||||
`
|
||||
</script>
|
||||
@@ -1,367 +0,0 @@
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<div class="pb-10 border-b u-border-gray-200 mb-10">
|
||||
<div>
|
||||
<h1 class="inline-block text-3xl font-extrabold u-text-gray-900 tracking-tight">
|
||||
Examples
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<p class="mt-1 text-lg u-text-gray-500">
|
||||
Examples of real-life usage of components.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="font-medium text-sm mb-1 u-text-gray-700">
|
||||
Avatar:
|
||||
</div>
|
||||
|
||||
<UAvatar src="https://picsum.photos/200/300" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="font-medium text-sm mb-1 u-text-gray-700">
|
||||
Button:
|
||||
</div>
|
||||
|
||||
<UButton variant="primary" icon="i-heroicons-envelope">
|
||||
Button text
|
||||
</UButton>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="font-medium text-sm mb-1 u-text-gray-700">
|
||||
Modal:
|
||||
</div>
|
||||
|
||||
<UButton @click="toggleModalIsOpen()">
|
||||
Toggle modal!
|
||||
</UButton>
|
||||
|
||||
<UModal v-model="isModalOpen" @submit.prevent="onSubmit">
|
||||
<div class="sm:flex sm:items-start">
|
||||
<div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
|
||||
<UIcon name="i-heroicons-exclamation" class="h-6 w-6 text-red-600" aria-hidden="true" />
|
||||
</div>
|
||||
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||
<h3 class="text-lg leading-6 font-medium u-text-gray-900">
|
||||
Deactivate account
|
||||
</h3>
|
||||
<div class="mt-2">
|
||||
<p class="text-sm u-text-gray-500">
|
||||
Are you sure you want to deactivate your account? All of your data will be permanently removed from our servers forever. This action cannot be undone.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
|
||||
<button type="submit" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm" @click="open = false">
|
||||
Deactivate
|
||||
</button>
|
||||
<button ref="cancelButtonRef" type="button" class="mt-3 w-full inline-flex justify-center rounded-md border u-border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium u-text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:w-auto sm:text-sm" @click="open = false">
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</UModal>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="font-medium text-sm mb-1 u-text-gray-700">
|
||||
Dropdown:
|
||||
</div>
|
||||
|
||||
<UDropdown v-slot="{ open }" :items="dropdownItems" placement="bottom-start">
|
||||
<UButton variant="white" :icon="open ? 'i-heroicons-chevron-up-20-solid' : 'i-heroicons-chevron-down-20-solid'" trailing icon-class="transition">
|
||||
Open menu!
|
||||
</UButton>
|
||||
</UDropdown>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="font-medium text-sm mb-1 u-text-gray-700">
|
||||
Dropdown with avatar:
|
||||
</div>
|
||||
|
||||
<UDropdown :items="customDropdownItems" placement="bottom-end">
|
||||
<button class="flex">
|
||||
<UAvatar src="https://picsum.photos/200/300" />
|
||||
</button>
|
||||
</UDropdown>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="font-medium text-sm mb-1 u-text-gray-700">
|
||||
Popover:
|
||||
</div>
|
||||
|
||||
<UPopover wrapper-class="inline-block relative">
|
||||
<template #default="{ open }">
|
||||
<UButton variant="secondary" :icon="open ? 'i-heroicons-chevron-up-20-solid-20' : 'i-heroicons-chevron-down-20-solid'" trailing icon-class="transition">
|
||||
Open popover!
|
||||
</UButton>
|
||||
</template>
|
||||
|
||||
<template #panel>
|
||||
<div class="p-2">
|
||||
Panel
|
||||
</div>
|
||||
</template>
|
||||
</UPopover>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="font-medium text-sm mb-1 u-text-gray-700">
|
||||
Tooltip:
|
||||
</div>
|
||||
|
||||
<UTooltip text="Hello tooltip!" :shortcuts="['⌘', 'G']">
|
||||
<UIcon name="i-heroicons-information-circle" class="w-6 h-6 u-text-gray-900 cursor-pointer" />
|
||||
</UTooltip>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="font-medium text-sm mb-1 u-text-gray-700">
|
||||
Notifications:
|
||||
</div>
|
||||
<UButton icon="i-heroicons-bell" variant="red" label="Trigger an error" @click="onNotificationClick" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="font-medium text-sm mb-1 u-text-gray-700">
|
||||
Copy text to clipboard:
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<UInput v-model="textToCopy" name="textToCopy" />
|
||||
<UButton icon="i-heroicons-document-duplicate-solid" variant="primary" label="Copy text" @click="onCopyTextClick" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="font-medium text-sm mb-1 u-text-gray-700">
|
||||
Context menu:
|
||||
</div>
|
||||
|
||||
<UCard class="relative" body-class="h-64" @click="isContextMenuOpen = false" @contextmenu.prevent="openContextMenu">
|
||||
<UContextMenu v-model="isContextMenuOpen" :virtual-element="virtualElement" width-class="w-48">
|
||||
<div class="p-2">
|
||||
Menu
|
||||
</div>
|
||||
</UContextMenu>
|
||||
</UCard>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="font-medium text-sm mb-1 u-text-gray-700">
|
||||
Command palette:
|
||||
</div>
|
||||
|
||||
<UCard body-class="">
|
||||
<UCommandPalette
|
||||
:placeholder="false"
|
||||
:options="{
|
||||
fuseOptions: {
|
||||
includeMatches: true
|
||||
}
|
||||
}"
|
||||
:groups="commandPaletteGroups"
|
||||
command-attribute="name"
|
||||
/>
|
||||
</UCard>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="font-medium text-sm mb-1 u-text-gray-700">
|
||||
Card:
|
||||
</div>
|
||||
|
||||
<UCard body-class="flex">
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 space-y-3">
|
||||
<UFormGroup label="Email" name="email" required>
|
||||
<UInput v-model="form.email" type="email" name="email" required icon="i-heroicons-mail" />
|
||||
</UFormGroup>
|
||||
|
||||
<UFormGroup label="Description" name="description">
|
||||
<UTextarea v-model="form.description" type="description" name="description" autoresize />
|
||||
</UFormGroup>
|
||||
|
||||
<UFormGroup label="Person" name="person" required>
|
||||
<USelect
|
||||
v-model="form.personId"
|
||||
name="person"
|
||||
:options="people"
|
||||
placeholder="Select a person"
|
||||
text-attribute="name"
|
||||
value-attribute="id"
|
||||
icon="i-heroicons-user"
|
||||
/>
|
||||
</UFormGroup>
|
||||
|
||||
<UFormGroup label="People" name="people" required>
|
||||
<USelectCustom v-model="form.person" name="people" :options="people" text-attribute="name" searchable />
|
||||
</UFormGroup>
|
||||
|
||||
<UFormGroup label="Toggle" name="toggle">
|
||||
<UToggle v-model="form.toggle" name="toggle" icon-off="i-heroicons-x-mark-20-solid" icon-on="i-heroicons-check-20-solid" />
|
||||
</UFormGroup>
|
||||
|
||||
<UFormGroup label="Notifications" label-class="text-base font-medium u-text-gray-900" description="How do you prefer to receive notifications?">
|
||||
<div class="space-y-4 mt-3">
|
||||
<URadio v-model="form.notification" value="email" label="Email" help="Email" />
|
||||
<URadio v-model="form.notification" value="phone" label="Phone (SMS)" help="Phone (SMS)" />
|
||||
<URadio v-model="form.notification" value="push" label="Push notification" help="Push notification" />
|
||||
</div>
|
||||
</UFormGroup>
|
||||
|
||||
<UCard body-class="px-4 py-5 space-y-5">
|
||||
<UCheckbox v-model="form.notifications" name="comments" value="comments" label="Comments" help="Get notified when someones posts a comment on a posting." />
|
||||
<UCheckbox v-model="form.notifications" name="candidates" value="candidates" label="Candidates" help="Get notified when a candidate applies for a job." />
|
||||
<UCheckbox v-model="form.notifications" name="offers" value="offers" label="Offers" help="Get notified when a candidate accepts or rejects an offer." />
|
||||
</UCard>
|
||||
|
||||
<div>
|
||||
<UCheckbox v-model="form.terms" label="I agree to the terms and conditions" name="terms" />
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end">
|
||||
<UButton type="submit" label="Submit" class="ml-auto" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-1/3 px-4 py-5 sm:p-6 border-l u-border-gray-200 u-bg-gray-50">
|
||||
<pre class="whitespace-pre-wrap break-all">
|
||||
{{ form }}
|
||||
</pre>
|
||||
</div>
|
||||
</UCard>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const isModalOpen = ref(false)
|
||||
const textToCopy = ref('Copied text')
|
||||
|
||||
const people = ref([
|
||||
{ id: 1, name: 'Durward Reynolds', disabled: false },
|
||||
{ id: 2, name: 'Kenton Towne', disabled: false },
|
||||
{ id: 3, name: 'Therese Wunsch', disabled: false },
|
||||
{ id: 4, name: 'Benedict Kessler', disabled: true },
|
||||
{ id: 5, name: 'Katelyn Rohan', disabled: false }
|
||||
])
|
||||
|
||||
const form = reactive({
|
||||
email: '',
|
||||
description: '',
|
||||
toggle: false,
|
||||
notification: 'email',
|
||||
notifications: [],
|
||||
terms: false,
|
||||
personId: null,
|
||||
person: ref(people.value[0]),
|
||||
persons: ref([people.value[0]])
|
||||
})
|
||||
|
||||
const toast = useToast()
|
||||
const clipboard = useCopyToClipboard()
|
||||
|
||||
const x = ref(0)
|
||||
const y = ref(0)
|
||||
const isContextMenuOpen = ref(false)
|
||||
const virtualElement = ref({ getBoundingClientRect: () => ({}) })
|
||||
|
||||
const commandPaletteGroups = computed(() => ([{
|
||||
key: 'people',
|
||||
commands: people.value
|
||||
}, {
|
||||
key: 'search',
|
||||
label: q => q && `Search results for "${q}"...`,
|
||||
search: async (q) => {
|
||||
if (!q) { return [] }
|
||||
return await $fetch(`https://jsonplaceholder.typicode.com/users?q=${q}`)
|
||||
}
|
||||
}]))
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('mousemove', ({ clientX, clientY }) => {
|
||||
x.value = clientX
|
||||
y.value = clientY
|
||||
})
|
||||
})
|
||||
|
||||
function openContextMenu () {
|
||||
const top = unref(y)
|
||||
const left = unref(x)
|
||||
|
||||
virtualElement.value.getBoundingClientRect = () => ({
|
||||
width: 0,
|
||||
height: 0,
|
||||
top,
|
||||
left
|
||||
})
|
||||
isContextMenuOpen.value = true
|
||||
}
|
||||
|
||||
function toggleModalIsOpen () {
|
||||
isModalOpen.value = !isModalOpen.value
|
||||
}
|
||||
|
||||
function onClick () {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('click')
|
||||
}
|
||||
|
||||
function onSubmit () {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('submit')
|
||||
}
|
||||
|
||||
const dropdownItems = [
|
||||
[{
|
||||
label: 'Edit',
|
||||
icon: 'i-heroicons-pencil-square-20-solid',
|
||||
click: (e) => {
|
||||
e.preventDefault()
|
||||
onClick()
|
||||
}
|
||||
}, {
|
||||
label: 'Duplicate',
|
||||
icon: 'i-heroicons-document-duplicate-20-solid'
|
||||
}],
|
||||
[{
|
||||
label: 'Archive',
|
||||
icon: 'i-heroicons-archive-box-20-solid'
|
||||
}, {
|
||||
label: 'Move',
|
||||
icon: 'i-heroicons-arrow-right-circle-20-solid',
|
||||
to: 'https://www.google.fr',
|
||||
target: '_blank'
|
||||
}],
|
||||
[{
|
||||
label: 'Delete',
|
||||
icon: 'i-heroicons-trash-20-solid'
|
||||
}]
|
||||
]
|
||||
|
||||
const customDropdownItems = [
|
||||
[{
|
||||
label: 'benjamincanac',
|
||||
avatar: { src: 'https://picsum.photos/200/300' },
|
||||
href: 'https://google.fr',
|
||||
target: '_blank'
|
||||
}],
|
||||
[{
|
||||
label: 'About',
|
||||
icon: 'i-heroicons-plus-20-solid',
|
||||
to: '/about'
|
||||
}]
|
||||
]
|
||||
|
||||
const onNotificationClick = () => {
|
||||
toast.error({ title: 'Error', description: 'This is an error message' })
|
||||
}
|
||||
|
||||
const onCopyTextClick = () => {
|
||||
clipboard.copy(textToCopy.value, { title: 'Text copied successfully!' })
|
||||
}
|
||||
</script>
|
||||
@@ -1,117 +1,7 @@
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<div class="pb-10 border-b u-border-gray-200 mb-10">
|
||||
<div>
|
||||
<h1 class="inline-block text-3xl font-extrabold u-text-gray-900 tracking-tight">
|
||||
Documentation
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<p class="mt-1 text-lg u-text-gray-500">
|
||||
Components library as a Nuxt3 module using <a href="https://tailwindcss.com" target="_blank" class="underline">TailwindCSS</a>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h2 class="font-bold text-2xl u-text-gray-900">
|
||||
Installation
|
||||
</h2>
|
||||
|
||||
<pre class="u-bg-gray-900 rounded-md u-text-white px-4">
|
||||
<code class="text-sm" v-html="code1" />
|
||||
</pre>
|
||||
|
||||
<p>Then, register the module in your `nuxt.config.js`:</p>
|
||||
|
||||
<pre class="u-bg-gray-900 rounded-md u-text-white px-4">
|
||||
<code class="text-sm" v-html="code2" />
|
||||
</pre>
|
||||
|
||||
<p>If you want latest updates, please use `@nuxthq/ui-edge` in your `package.json`:</p>
|
||||
|
||||
<pre class="u-bg-gray-900 rounded-md u-text-white px-4">
|
||||
<code class="text-sm" v-html="code3" />
|
||||
</pre>
|
||||
|
||||
<h2 class="font-bold text-2xl u-text-gray-900">
|
||||
Options
|
||||
</h2>
|
||||
|
||||
<p>- `preset`</p>
|
||||
|
||||
<p>Choose preset. Defaults to `tailwindui`. An object can also be used to override some parts of the default preset.</p>
|
||||
|
||||
<p>- `prefix`</p>
|
||||
|
||||
<p>Define the prefix of the imported components. Defaults to `u`.</p>
|
||||
|
||||
<p class="font-medium">
|
||||
Example:
|
||||
</p>
|
||||
|
||||
<pre class="u-bg-gray-900 rounded-md u-text-white px-4">
|
||||
<code class="text-sm" v-html="code4" />
|
||||
</pre>
|
||||
|
||||
<p>- `colors.primary`</p>
|
||||
|
||||
<p>Define the primary variant. Defaults to `indigo`. You can specify your own object of colors like here:</p>
|
||||
|
||||
<p class="font-medium">
|
||||
Example:
|
||||
</p>
|
||||
|
||||
<pre class="u-bg-gray-900 rounded-md u-text-white px-4">
|
||||
<code class="text-sm" v-html="code5" />
|
||||
</pre>
|
||||
|
||||
<p>- `colors.gray`</p>
|
||||
|
||||
<p>Define the gray variant. Defaults to `zinc`. You can like the `primary` color specify your own object. https://tailwindcss.com/docs/customizing-colors#default-color-palette</p>
|
||||
|
||||
<p>- `tailwindcss.theme`. Defaults to `{}`.</p>
|
||||
|
||||
<p>Define TailwindCSS theme: https://tailwindcss.com/docs/theme.</p>
|
||||
</div>
|
||||
<div />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const code1 = `
|
||||
yarn add --dev @nuxthq/ui`
|
||||
|
||||
const code2 = `
|
||||
import { defineNuxtConfig } from 'nuxt'
|
||||
|
||||
defineNuxtConfig({
|
||||
buildModules: [
|
||||
'@nuxthq/ui'
|
||||
]
|
||||
})`
|
||||
|
||||
const code3 = `
|
||||
{
|
||||
"devDependencies": {
|
||||
"@nuxthq/ui": "npm:@nuxthq/ui-edge@latest"
|
||||
}
|
||||
}`
|
||||
|
||||
const code4 = `
|
||||
import { defineNuxtConfig } from 'nuxt'
|
||||
|
||||
defineNuxtConfig({
|
||||
ui: {
|
||||
prefix: 'tw'
|
||||
}
|
||||
})`
|
||||
|
||||
const code5 = `
|
||||
import { defineNuxtConfig } from 'nuxt'
|
||||
|
||||
defineNuxtConfig({
|
||||
ui: {
|
||||
colors: {
|
||||
primary: 'blue'
|
||||
}
|
||||
}
|
||||
})`
|
||||
<script setup lang="ts">
|
||||
await navigateTo('/getting-started/installation')
|
||||
</script>
|
||||
|
||||
@@ -1,285 +0,0 @@
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<div class="pb-10 border-b u-border-gray-200 mb-10">
|
||||
<div>
|
||||
<h1 class="inline-block text-3xl font-extrabold u-text-gray-900 tracking-tight">
|
||||
Migration
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<p class="mt-1 text-lg u-text-gray-500">
|
||||
Check out the components that have been migrated to Vue3 coming from `@nuxthq/volta-ui`.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<UCard body-class>
|
||||
<table class="min-w-full divide-y u-divide-gray-200">
|
||||
<thead class="u-bg-gray-50">
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium u-text-gray-500 uppercase tracking-wider">
|
||||
Component
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium u-text-gray-500 uppercase tracking-wider">
|
||||
Nuxt3 ready
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium u-text-gray-500 uppercase tracking-wider">
|
||||
Composition API
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium u-text-gray-500 uppercase tracking-wider">
|
||||
Preset system
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium u-text-gray-500 uppercase tracking-wider">
|
||||
Typescript
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(component, index) of components" :key="index" :class="index % 2 === 0 ? 'u-bg-white' : 'u-bg-gray-50'">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium u-text-gray-900">
|
||||
<NuxtLink :to="component.to" class="hover:underline">
|
||||
{{ component.label }}
|
||||
</NuxtLink>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm u-text-gray-500">
|
||||
<span v-if="component.nuxt3 || component.capi">✅</span>
|
||||
<span v-else>❌</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm u-text-gray-500">
|
||||
<span v-if="component.capi">✅</span>
|
||||
<span v-else>❌</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm u-text-gray-500">
|
||||
<span v-if="component.preset">✅</span>
|
||||
<span v-else>❌</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm u-text-gray-500">
|
||||
<span v-if="component.typescript">✅</span>
|
||||
<span v-else>❌</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</UCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const components = [
|
||||
{
|
||||
label: 'Avatar',
|
||||
to: '/components/Avatar',
|
||||
nuxt3: true,
|
||||
preset: true,
|
||||
capi: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'AvatarGroup',
|
||||
to: '/components/AvatarGroup',
|
||||
nuxt3: true,
|
||||
capi: true,
|
||||
preset: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'Badge',
|
||||
to: '/components/Badge',
|
||||
nuxt3: true,
|
||||
capi: true,
|
||||
preset: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'Button',
|
||||
to: '/components/Button',
|
||||
nuxt3: true,
|
||||
capi: true,
|
||||
preset: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'Dropdown',
|
||||
to: '/components/Dropdown',
|
||||
nuxt3: true,
|
||||
capi: true,
|
||||
preset: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'Link',
|
||||
to: '/components/Link',
|
||||
nuxt3: true,
|
||||
capi: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'Toggle',
|
||||
to: '/components/Toggle',
|
||||
nuxt3: true,
|
||||
preset: true,
|
||||
capi: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'Alert',
|
||||
to: '/components/Alert',
|
||||
nuxt3: true,
|
||||
capi: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'AlertDialog',
|
||||
to: '/components/AlertDialog',
|
||||
nuxt3: true,
|
||||
capi: true,
|
||||
preset: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'Input',
|
||||
to: '/components/Input',
|
||||
capi: true,
|
||||
preset: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'FormGroup',
|
||||
to: '/components/FormGroup',
|
||||
nuxt3: true,
|
||||
capi: true,
|
||||
preset: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'Checkbox',
|
||||
to: '/components/Checkbox',
|
||||
nuxt3: true,
|
||||
capi: true,
|
||||
preset: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'Radio',
|
||||
to: '/components/Radio',
|
||||
nuxt3: true,
|
||||
capi: true,
|
||||
preset: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'Select',
|
||||
to: '/components/Select',
|
||||
nuxt3: true,
|
||||
capi: true,
|
||||
preset: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'SelectCustom',
|
||||
to: '/components/SelectCustom',
|
||||
capi: true,
|
||||
preset: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'Textarea',
|
||||
to: '/components/Textarea',
|
||||
nuxt3: true,
|
||||
capi: true,
|
||||
preset: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'Card',
|
||||
to: '/components/Card',
|
||||
nuxt3: true,
|
||||
capi: true,
|
||||
preset: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'Container',
|
||||
to: '/components/Container',
|
||||
nuxt3: true,
|
||||
preset: true,
|
||||
capi: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'CommandPalette',
|
||||
to: '/components/CommandPalette',
|
||||
nuxt3: true,
|
||||
capi: true,
|
||||
preset: false,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'Tabs',
|
||||
to: '/components/Tabs',
|
||||
nuxt3: true,
|
||||
capi: true,
|
||||
preset: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'Pills',
|
||||
to: '/components/Pills',
|
||||
nuxt3: true,
|
||||
capi: true,
|
||||
preset: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'VerticalNavigation',
|
||||
to: '/components/VerticalNavigation',
|
||||
nuxt3: true,
|
||||
capi: true,
|
||||
preset: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'Modal',
|
||||
to: '/components/Modal',
|
||||
nuxt3: true,
|
||||
preset: true,
|
||||
capi: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'Notification',
|
||||
to: '/components/Notification',
|
||||
nuxt3: true,
|
||||
capi: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'Notifications',
|
||||
to: '/components/Notifications',
|
||||
nuxt3: true,
|
||||
capi: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'Popover',
|
||||
to: '/components/Popover',
|
||||
nuxt3: true,
|
||||
capi: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'Slideover',
|
||||
to: '/components/Slideover',
|
||||
nuxt3: true,
|
||||
capi: true,
|
||||
preset: true,
|
||||
typescript: true
|
||||
},
|
||||
{
|
||||
label: 'Tooltip',
|
||||
to: '/components/Tooltip',
|
||||
nuxt3: true,
|
||||
capi: true,
|
||||
typescript: true
|
||||
}
|
||||
]
|
||||
</script>
|
||||
Reference in New Issue
Block a user