Compare commits

...

20 Commits

Author SHA1 Message Date
Benjamin Canac
066b239299 chore(release): 2.0.4 2023-05-15 14:08:04 +02:00
Benjamin Canac
97da6c6343 chore(SelectMenu): add required input only when needed 2023-05-15 14:07:41 +02:00
Benjamin Canac
6d9e4d424b docs: improve alert code blocks on light mode 2023-05-15 14:07:23 +02:00
Benjamin Canac
52192a4ac0 docs: add input group section 2023-05-15 14:07:14 +02:00
Benjamin Canac
a6c53e4d20 chore(app.config): improve forms required 2023-05-15 14:07:02 +02:00
Benjamin Canac
e8b46540d8 fix(SelectMenu): add missing inline-flex on wrapper 2023-05-15 12:15:11 +02:00
Benjamin Canac
a01e0279a9 chore(release): 2.0.3 2023-05-15 11:22:09 +02:00
Benjamin Canac
ccbf3e78b1 docs: typo in button 2023-05-15 11:21:51 +02:00
Benjamin Canac
dcc0a6b3a9 docs: minor improvements 2023-05-14 17:07:11 +02:00
Benjamin Canac
bb4cd0b1b9 docs: add CommandPalette full-text search section 2023-05-14 17:06:31 +02:00
Benjamin Canac
ae02f23a8c chore(Kbd): usage with value prop 2023-05-14 17:06:08 +02:00
Benjamin Canac
8caa78819a chore(Skeleton): new component 2023-05-14 17:05:38 +02:00
Benjamin Canac
6fd5a70ac9 docs: improve ContextMenu example 2023-05-14 15:08:16 +02:00
Benjamin Canac
cfcd2f1371 docs: uniformize isOpen in examples 2023-05-14 15:05:33 +02:00
Benjamin Canac
c47f5e6a68 docs: improve forms cards 2023-05-14 15:04:56 +02:00
Benjamin Canac
37f1a1b5ad docs: lots of improvements 2023-05-13 23:22:07 +02:00
Benjamin Canac
0c2a5d98cf chore(SelectMenu): use trailingIcon default from Select for consistency 2023-05-13 23:21:32 +02:00
Benjamin Canac
aabf1dd9eb chore(release): 2.0.2 2023-05-11 17:12:50 +02:00
Benjamin Canac
c7c78cb47b fix(LinkCustom): handle button when no to prop 2023-05-11 17:03:02 +02:00
Benjamin Canac
3335a6a32c docs: put back router.options.ts 2023-05-11 16:51:30 +02:00
39 changed files with 883 additions and 125 deletions

View File

@@ -2,6 +2,22 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [2.0.4](https://github.com/nuxtlabs/ui/compare/v2.0.3...v2.0.4) (2023-05-15)
### Bug Fixes
* **SelectMenu:** add missing `inline-flex` on wrapper ([e8b4654](https://github.com/nuxtlabs/ui/commit/e8b46540d8767c7a63c0ff8e28263615626916e7))
### [2.0.3](https://github.com/nuxtlabs/ui/compare/v2.0.2...v2.0.3) (2023-05-15)
### [2.0.2](https://github.com/nuxtlabs/ui/compare/v2.0.1...v2.0.2) (2023-05-11)
### Bug Fixes
* **LinkCustom:** handle `button` when no `to` prop ([c7c78cb](https://github.com/nuxtlabs/ui/commit/c7c78cb47b00963c8a9ea0c0599fbc7e128cff66))
### [2.0.1](https://github.com/nuxtlabs/ui/compare/v2.0.0...v2.0.1) (2023-05-11)

View File

@@ -1,38 +1,48 @@
import type { RouterConfig } from '@nuxt/schema'
// https://router.vuejs.org/api/interfaces/routeroptions.html
export default <RouterConfig> {
scrollBehavior (to, _form, savedPosition) {
if (history.state.stop) { return }
if (history.state.smooth) {
return {
el: history.state.smooth,
behavior: 'smooth'
}
}
function findHashPosition (hash): { el: any, behavior: ScrollBehavior, top: number } {
const el = document.querySelector(hash)
// vue-router does not incorporate scroll-margin-top on its own.
if (el) {
const top = parseFloat(getComputedStyle(el).scrollMarginTop)
if (to.hash) {
const el = document.querySelector(to.hash) as any
if (!el) { return }
const { marginTop } = getComputedStyle(el)
const marginTopValue = parseInt(marginTop)
const offset = (document.querySelector(to.hash) as any).offsetTop - marginTopValue
return {
top: offset,
behavior: 'smooth'
}
}
// Scroll to top of window
if (savedPosition) {
return savedPosition
} else {
return { top: 0 }
return {
el: hash,
behavior: 'smooth',
top
}
}
}
// https://router.vuejs.org/api/#routeroptions
export default <RouterConfig>{
scrollBehavior (to, from, savedPosition) {
const nuxtApp = useNuxtApp()
// If history back
if (savedPosition) {
// Handle Suspense resolution
return new Promise((resolve) => {
nuxtApp.hooks.hookOnce('page:finish', () => {
setTimeout(() => resolve(savedPosition), 50)
})
})
}
// Scroll to heading on click
if (to.hash) {
return new Promise((resolve) => {
if (to.path === from.path) {
setTimeout(() => resolve(findHashPosition(to.hash)), 50)
} else {
nuxtApp.hooks.hookOnce('page:finish', () => {
setTimeout(() => resolve(findHashPosition(to.hash)), 50)
})
}
})
}
// Scroll to top of window
return { top: 0 }
}
}

View File

@@ -2,7 +2,7 @@
<component
:is="to ? NuxtLink : 'div'"
:to="to"
class="block pl-4 pr-6 py-3 rounded-md !border !border-gray-200 dark:!border-gray-700 bg-gray-50 dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:text-gray-800 dark:hover:text-gray-200 text-sm leading-6 my-5 last:mb-0 font-normal group relative prose-code:bg-gray-200 dark:prose-code:bg-gray-800"
class="block pl-4 pr-6 py-3 rounded-md !border !border-gray-200 dark:!border-gray-700 bg-gray-50 dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:text-gray-800 dark:hover:text-gray-200 text-sm leading-6 my-5 last:mb-0 font-normal group relative"
:class="[to ? 'hover:!border-primary-500 dark:hover:!border-primary-400 hover:text-primary-500 dark:hover:text-primary-400 border-dashed' : '']"
>
<UIcon v-if="!!to" name="i-heroicons-link-20-solid" class="w-3 h-3 absolute right-2 top-2 text-gray-400 dark:text-gray-500 group-hover:text-primary-500 dark:group-hover:text-primary-400" />

View File

@@ -1,9 +1,7 @@
<template>
<kbd class="inline-flex items-center justify-center font-sans font-semibold px-1 h-5 min-w-[20px] text-[11px] rounded !my-0 align-text-top ring-1 ring-gray-300 dark:ring-gray-700">
<ClientOnly>
{{ shortcut }}
</ClientOnly>
</kbd>
<UKbd class="!my-0 align-text-top">
{{ shortcut }}
</UKbd>
</template>
<script setup lang="ts">

View File

@@ -1,5 +1,5 @@
<script setup>
const open = ref(false)
const isOpen = ref(false)
const people = [
{ id: 1, label: 'Wade Cooper' },
@@ -19,9 +19,9 @@ const selected = ref([])
<template>
<div>
<UButton label="Open" @click="open = true" />
<UButton label="Open" @click="isOpen = true" />
<UModal v-model="open">
<UModal v-model="isOpen">
<UCommandPalette
v-model="selected"
multiple

View File

@@ -1,11 +1,12 @@
<script setup>
const { x, y } = useMouse()
const { y: windowY } = useWindowScroll()
const isOpen = ref(false)
const virtualElement = ref({ getBoundingClientRect: () => ({}) })
function openContextMenu () {
const top = unref(y)
function onContextMenu () {
const top = unref(y) - unref(windowY)
const left = unref(x)
virtualElement.value.getBoundingClientRect = () => ({
@@ -20,8 +21,8 @@ function openContextMenu () {
</script>
<template>
<div class="w-full" @contextmenu.prevent="openContextMenu">
<Placeholder class="h-20 w-full flex items-center justify-center">
<div class="w-full" @contextmenu.prevent="onContextMenu">
<Placeholder class="h-96 select-none w-full flex items-center justify-center">
Right click here
</Placeholder>

View File

@@ -5,6 +5,6 @@ const { metaSymbol } = useShortcuts()
<template>
<div class="flex items-center gap-0.5">
<UKbd>{{ metaSymbol }}</UKbd>
<UKbd>U</UKbd>
<UKbd>K</UKbd>
</div>
</template>

View File

@@ -1,12 +1,12 @@
<script setup>
const open = ref(false)
const isOpen = ref(false)
</script>
<template>
<div>
<UButton label="Open" @click="open = true" />
<UButton label="Open" @click="isOpen = true" />
<UModal v-model="open">
<UModal v-model="isOpen">
<div class="p-4">
<Placeholder class="h-48" />
</div>

View File

@@ -1,12 +1,12 @@
<script setup>
const open = ref(false)
const isOpen = ref(false)
</script>
<template>
<div>
<UButton label="Open" @click="open = true" />
<UButton label="Open" @click="isOpen = true" />
<UModal v-model="open">
<UModal v-model="isOpen">
<UCard :ui="{ ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
<template #header>
<Placeholder class="h-8" />

View File

@@ -0,0 +1,9 @@
<template>
<div class="flex items-center space-x-4">
<USkeleton class="h-12 w-12" :ui="{ rounded: 'rounded-full' }" />
<div class="space-y-2">
<USkeleton class="h-4 w-[250px]" />
<USkeleton class="h-4 w-[200px]" />
</div>
</div>
</template>

View File

@@ -1,12 +1,12 @@
<script setup>
const open = ref(false)
const isOpen = ref(false)
</script>
<template>
<div>
<UButton label="Open" @click="open = true" />
<UButton label="Open" @click="isOpen = true" />
<USlideover v-model="open">
<USlideover v-model="isOpen">
<div class="p-4 h-full">
<Placeholder class="w-full h-full" />
</div>

View File

@@ -0,0 +1,33 @@
---
title: Introduction
description: 'Components library as a Nuxt module using TailwindCSS and HeadlessUI.'
---
This module has been developed by the [NuxtLabs](https://nuxtlabs.com/) team for [Volta](https://volta.net) and [Nuxt Studio](https://nuxt.studio/), its goal is to provide everything related to UI when building a Nuxt app. This includes components, icons, colors, dark mode but also keyboard shortcuts.
::alert{icon="i-heroicons-light-bulb"}
[Volta](https://volta.net/) entire UI is built with this module alongside the 50+ keyboard shortcuts defined.
::
## Features
- Fully styled and customizable components
- HMR support through Nuxt App Config
- Dark mode support
- Keyboard shortcuts
- Bundled icons
- Fully typed
## Credits
- [nuxt/nuxt](https://github.com/nuxt/nuxt)
- [nuxt-modules/color-mode](https://github.com/nuxt-modules/color-mode)
- [nuxt-modules/tailwindcss](https://github.com/nuxt-modules/tailwindcss)
- [tailwindlabs/tailwindcss](https://github.com/tailwindlabs/tailwindcss)
- [tailwindlabs/headlessui](https://github.com/tailwindlabs/headlessui)
- [vueuse/vueuse](https://github.com/vueuse/vueuse)
- [egoist/tailwindcss-icons](https://github.com/egoist/tailwindcss-icons)
::alert{icon="i-heroicons-exclamation-triangle"}
This documentation is still a work in progress and will be updated regularly.
::

View File

@@ -1,11 +1,8 @@
---
title: 'nuxthq/ui'
description: 'Components library as a Nuxt module using TailwindCSS and HeadlessUI.'
navigation:
title: Installation
description: 'Learn how to install and configure the module in your Nuxt app.'
---
## Installation
## Overview
1. Install `@nuxthq/ui` dependency to your project:

View File

@@ -1,5 +0,0 @@
---
navigation: false
---
## Overview

View File

@@ -1,11 +0,0 @@
---
navigation: false
---
## Overview
## Composables
## `defineShortcuts`
## `useShortcuts`

View File

@@ -0,0 +1,167 @@
---
description: 'Learn how to customize the look and feel of the components.'
---
## Overview
This module relies on Nuxt [App Config](https://nuxt.com/docs/guide/directory-structure/app-config#app-config-file) file to customize the look and feel of the components at runtime with HMR (hot-module-replacement).
## Colors
Components are based on a `primary` and a `gray` color. You can change them in your `app.config.ts`.
```ts [app.config.ts]
export default defineAppConfig({
ui: {
primary: 'sky',
gray: 'cool'
}
})
```
As this module uses TailwindCSS under the hood, you can use any of the [TailwindCSS colors](https://tailwindcss.com/docs/customizing-colors#color-palette-reference) or your own custom colors. By default, the `primary` color is `sky` and the `gray` color is `cool`.
To provide dynamic colors that can be changed at runtime, this module uses CSS variables. As TailwindCSS already has a `gray` color, the module automatically renames it to `cool` to avoid conflicts (`coolGray` was renamed to `gray` when Tailwind CSS v3.0 was released).
::alert{icon="i-heroicons-light-bulb"}
Try to change the `primary` and `gray` colors in the navbar and see the colors change live.
::
Components that have a `color` prop like [Avatar](/elements/avatar), [Badge](/elements/badge) and [Button](/elements/button) will use the `primary` color by default but will handle all the colors defined in your `tailwind.config.ts` or the default TailwindCSS colors.
## Components
Components are styled with TailwindCSS but classes are all defined in the default [app.config.ts](https://github.com/nuxtlabs/ui/blob/dev/src/runtime/app.config.ts) file. You can override them in your `app.config.ts`.
```ts [app.config.ts]
export default defineAppConfig({
ui: {
container: {
constrained: 'max-w-5xl'
}
}
})
```
Each component has a `ui` prop that allows you to customize everything specifically.
```vue
<template>
<UContainer :ui="{ constrained: 'max-w-2xl' }">
<slot />
</UContainer>
</template>
```
::alert{icon="i-heroicons-light-bulb"}
You can find the default classes for each component under the `Preset` section.
::
Some component props like `size`, `color`, `variant`, etc. have a default value that you can override in your `app.config.ts`.
```ts [app.config.ts]
export default defineAppConfig({
ui: {
button: {
default: {
size: 'md',
color: 'gray',
variant: 'ghost'
}
}
}
})
```
## Icons
You can use any icon (100,000+) from [Iconify](https://iconify.design/).
Some components have an `icon` prop that allows you to add an icon to the component.
```vue
<template>
<UButton icon="i-heroicons-magnifying-glass" />
</template>
```
You can also use the [Icon](/elements/icon) component to add an icon anywhere in your app by following this pattern: `i-{collection_name}-{icon_name}`.
```vue
<template>
<UIcon name="i-heroicons-moon" />
</template>
```
By default, the module uses [Heroicons](https://heroicons.com/) but you can change it from the module options in your `nuxt.config.ts`.
```ts [nuxt.config.ts]
export default defineNuxtConfig({
ui: {
icons: ['mdi', 'simple-icons']
}
})
```
::alert{icon="i-heroicons-light-bulb"}
Search the icon you want to use on https://icones.js.org built by [@antfu](https://github.com/antfu).
::
Unlike the official [nuxt-icon](https://github.com/nuxt-modules/icon/) module, this module will not fetch any icon from the web and will only bundle the icons you use in your app thanks to [egoist/tailwindcss-icons](https://github.com/egoist/tailwindcss-icons).
However, you will need to install the icon packages you want to use.
```sh
yarn add --dev @iconify/json-{collection_name}
```
You can easily replace all the default icons of the components in your `app.config.ts`.
```ts [app.config.ts]
export default defineAppConfig({
ui: {
button: {
default: {
loadingIcon: 'i-octicon-sync-24'
}
},
input: {
default: {
loadingIcon: 'i-octicon-sync-24'
}
},
select: {
default: {
trailingIcon: 'i-octicon-chevron-down-24'
}
},
selectMenu: {
default: {
selectedIcon: 'i-octicon-check-24'
}
},
notification: {
default: {
close: {
icon: 'i-octicon-x-24'
}
}
},
commandPalette: {
default: {
icon: 'i-octicon-search-24',
selectedIcon: 'i-octicon-check-24',
empty: {
icon: 'i-octicon-search-24'
}
}
}
}
})
```
## Dark mode
All the components are styled with dark mode in mind.
Thanks to [TailwindCSS dark mode](https://tailwindcss.com/docs/dark-mode#toggling-dark-mode-manually) `class` strategy and the [@nuxtjs/color-mode](https://github.com/nuxt-modules/color-mode) module, you literally have nothing to do.

View File

@@ -0,0 +1,63 @@
---
description: 'Learn how to display and define keyboard shortcuts in your app.'
---
## Overview
Some components like [Dropdown](/elements/dropdown), [Command Palette](/navigation/command-palette) and [Tooltip](/overlays/tooltip) support the display of keyboard shortcuts.
```vue
<UDropdown :items="[{ label: 'Edit', shortcuts: ['E'] }]" />
```
Shortcuts are displayed and styled through the [Kbd](/elements/kbd) component.
```vue
<template>
<div class="flex items-center gap-0.5">
<UKbd></UKbd>
<UKbd>K</UKbd>
</div>
</template>
```
::alert{icon="i-heroicons-light-bulb"}
You will have a preview of how shortcuts are rendered in each component page.
::
## `defineShortcuts`
This module provides a `defineShortcuts` composable that allows you to define keyboard shortcuts in your app really easily.
```vue
<template>
<UModal v-model="isOpen" />
</template>
<script setup lang="ts">
const isOpen = ref(false)
defineShortcuts({
meta_k: {
usingInput: true,
handler: () => {
isOpen.value = !isOpen.value
}
}
})
</script>
```
## `useShortcuts`
To display shortcuts in your app according to the user's OS, you can use the `useShortcuts` composable.
```vue
<script setup>
const { metaSymbol } = useShortcuts()
</script>
<template>
<UKbd>{{ metaSymbol }}</UKbd>
</template>
```

View File

@@ -37,7 +37,7 @@ props:
Button
::
Besides all the colors from the `ui.colors` object, you can also use the `white` and `gray` and `black` colors with their pre-defined variants.
Besides all the colors from the `ui.colors` object, you can also use the `white`, `gray` and `black` colors with their pre-defined variants.
#### White
@@ -161,6 +161,8 @@ Button
Use the `loading` prop to show a loading icon and disable the Button.
Use the `loadingIcon` prop to set a different icon or change it globally in `ui.button.default.loadingIcon`. Defaults to `i-heroicons-arrow-path-20-solid`.
::component-card
---
props:

View File

@@ -0,0 +1,20 @@
---
github: true
---
## Usage
::component-card
---
props:
name: 'i-heroicons-light-bulb'
---
::
::alert{icon="i-heroicons-exclamation-triangle"}
When playing with the `name` prop above, you won't be able to use any icon you want as icons are bundled on build as explained in the [theming section](/getting-started/theming#icons).
::
## Props
:component-props

View File

@@ -7,6 +7,27 @@ navigation:
## Usage
Use the default slot to set the text of the Kbd.
::component-card
---
code: K
---
K
::
You can also use the `value` prop:
::component-card
---
props:
value: K
---
::
As explained in the [Shortcuts](/getting-started/shortcuts) page, you can use the `metaSymbol` property of the `useShortcuts` composable to display the meta key according to the user's OS.
::component-example
#default
:kbd-example
@@ -20,7 +41,7 @@ const { metaSymbol } = useShortcuts()
<template>
<div class="flex items-center gap-0.5">
<UKbd>{{ metaSymbol }}</UKbd>
<UKbd>U</UKbd>
<UKbd>K</UKbd>
</div>
</template>
```
@@ -34,6 +55,7 @@ Use the `size` prop to change the size of the Kbd.
---
props:
size: 'sm'
code: 'U'
---
U

View File

@@ -24,6 +24,33 @@ props:
---
::
### Placeholder
Use the `placeholder` prop to set a placeholder text.
::component-card
---
baseProps:
name: 'input'
props:
placeholder: 'Search...'
---
::
### Appearance
Use the `appearance` prop to change the style of the Input.
::component-card
---
baseProps:
name: 'input'
placeholder: 'Search...'
props:
appearance: 'white'
---
::
### Icon
Use any icon from [Iconify](https://icones.js.org) by setting the `icon` prop by using this pattern: `i-{collection_name}-{icon_name}`.
@@ -34,15 +61,14 @@ Use the `leading` and `trailing` props to set the icon position or the `leadingI
---
baseProps:
name: 'input'
placeholder: 'Search...'
props:
icon: 'i-heroicons-magnifying-glass-20-solid'
appearance: 'white'
size: 'sm'
trailing: false
placeholder: 'Search...'
excludedProps:
- icon
- placeholder
---
::
@@ -67,11 +93,13 @@ excludedProps:
Use the `loading` prop to show a loading icon and disable the Input.
Use the `loadingIcon` prop to set a different icon or change it globally in `ui.input.default.loadingIcon`. Defaults to `i-heroicons-arrow-path-20-solid`.
::component-card
---
baseProps:
name: 'input'
placeholder: 'Search'
placeholder: 'Searching...'
props:
loading: true
icon: 'i-heroicons-magnifying-glass-20-solid'
@@ -82,6 +110,30 @@ excludedProps:
### Group
You can use the `InputGroup` component to add a label and additional informations to a form element.
::component-card{slug="InputGroup"}
---
baseProps:
name: 'group'
props:
label: 'Email'
help: "We'll only use this for spam."
hint: 'Required'
required: true
code: >-
<UInput placeholder="you@example.com" icon="i-heroicons-envelope" />
---
#default
:u-input{name="group" placeholder="you@example.com" icon="i-heroicons-envelope"}
::
::alert{icon="i-heroicons-light-bulb"}
This also works with `Textarea`, `Select` and `SelectMenu` components.
::
## Props
:component-props

View File

@@ -11,6 +11,61 @@ baseProps:
---
::
### Size
Use the `size` prop to change the size of the Textarea.
::component-card
---
baseProps:
name: 'textarea'
props:
size: 'sm'
---
::
### Placeholder
Use the `placeholder` prop to set a placeholder text.
::component-card
---
baseProps:
name: 'textarea'
props:
placeholder: 'Search...'
---
::
### Appearance
Use the `appearance` prop to change the style of the Textarea.
::component-card
---
baseProps:
name: 'textarea'
placeholder: 'Search...'
props:
appearance: 'white'
---
::
### Disabled
Use the `disabled` prop to disable the Textarea.
::component-card
---
baseProps:
name: 'input'
placeholder: 'Search...'
props:
appearance: 'white'
disabled: true
---
::
## Props
:component-props

View File

@@ -8,11 +8,109 @@ github: true
---
baseProps:
name: 'select'
modelValue: 'Canada'
modelValue: 'United States'
props:
options:
- 'United States'
- 'Canada'
- 'Mexico'
excludedProps:
- options
---
::
### Size
Use the `size` prop to change the size of the Input.
::component-card
---
baseProps:
name: 'select'
options:
- 'United States'
- 'Canada'
- 'Mexico'
props:
size: 'sm'
---
::
### Placeholder
Use the `placeholder` prop to set a placeholder text.
::component-card
---
baseProps:
name: 'select'
options:
- 'United States'
- 'Canada'
- 'Mexico'
props:
placeholder: 'Search...'
---
::
### Appearance
Use the `appearance` prop to change the style of the Select.
::component-card
---
baseProps:
name: 'select'
options:
- 'United States'
- 'Canada'
- 'Mexico'
placeholder: 'Search...'
props:
appearance: 'white'
---
::
### Icon
Use any icon from [Iconify](https://icones.js.org) by setting the `icon` prop by using this pattern: `i-{collection_name}-{icon_name}`.
Use the `trailingIcon` prop to set a different icon or change it globally in `ui.select.default.trailingIcon`. Defaults to `i-heroicons-chevron-down-20-solid`.
::component-card
---
baseProps:
name: 'select'
options:
- 'United States'
- 'Canada'
- 'Mexico'
placeholder: 'Search...'
props:
icon: 'i-heroicons-magnifying-glass-20-solid'
appearance: 'white'
size: 'sm'
excludedProps:
- icon
---
::
### Disabled
Use the `disabled` prop to disable the Input.
::component-card
---
baseProps:
name: 'select'
options:
- 'United States'
- 'Canada'
- 'Mexico'
placeholder: 'Search...'
props:
appearance: 'white'
disabled: true
---
::

View File

@@ -7,6 +7,10 @@ headlessui:
## Usage
The SelectMenu component renders by default a [Select](/forms/select) component and is based on the `ui.select` preset. You can use most of the Select props to configure the display if you don't want to override the default slot such as [size](/forms/select#size), [placeholder](/forms/select#placeholder), [appearance](/forms/select#appearance), [icon](/forms/select#icon), [disabled](/forms/select#disabled), etc.
Like the Select component, you can use the `options` prop to pass an array of strings or objects.
::component-example
#default
:select-menu-example-basic{class="max-w-[12rem] w-full"}
@@ -25,7 +29,7 @@ const selected = ref(people[0])
```
::
You can use multiple values but you have to override the `#label` slot and handle the display yourself.
You can use the `multiple` prop to select multiple values but you have to override the `#label` slot and handle the display yourself.
::component-example
#default
@@ -76,7 +80,7 @@ const selected = ref(people[3])
```
::
You can pass an array of objects to `options` and either compare on the whole object or use the `by` prop to compare on a specific key.
You can pass an array of objects to `options` and either compare on the whole object or use the `by` prop to compare on a specific key. You can configure which field will be used to display the label through the `optionAttribute` prop that defaults to `label`.
::component-example
#default
@@ -131,6 +135,10 @@ const selected = ref(people[0])
Use any icon from [Iconify](https://icones.js.org) by setting the `icon` prop by using this pattern: `i-{collection_name}-{icon_name}`.
Use the `trailingIcon` prop to set a different icon or change it globally in `ui.select.default.trailingIcon`. Defaults to `i-heroicons-chevron-down-20-solid`.
Use the `selectedIcon` prop to set a different icon or change it globally in `ui.selectMenu.default.selectedIcon`. Defaults to `i-heroicons-check-20-solid`.
::component-card
---
baseProps:
@@ -161,21 +169,6 @@ props:
---
::
### Disabled
Use the `disabled` prop to disable the SelectMenu.
::component-card
---
baseProps:
class: 'max-w-[12rem] w-full'
placeholder: 'Select a person'
options: ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
props:
disabled: true
---
::
## Props
:component-props

View File

@@ -5,6 +5,65 @@ github: true
## Usage
::component-card
---
baseProps:
name: 'checkbox'
---
::
### Label
Use the `label` prop to display a label on the right.
::component-card
---
baseProps:
name: 'checkbox1'
props:
label: 'Label'
---
::
### Required
Use the `required` prop to display a red star next to the label.
::component-card
---
baseProps:
name: 'checkbox2'
props:
label: 'Label'
required: true
---
::
### Help
Use the `help` prop to display some text under the Checkbox.
::component-card
---
baseProps:
name: 'checkbox3'
props:
label: 'Label'
help: 'Please check this box'
---
::
### Disabled
Use the `disabled` prop to disable the Checkbox.
::component-card
---
baseProps:
name: 'checkbox4'
modelValue: true
props:
disabled: true
---
::
## Props

View File

@@ -5,6 +5,65 @@ github: true
## Usage
::component-card
---
baseProps:
name: 'radio'
---
::
### Label
Use the `label` prop to display a label on the right.
::component-card
---
baseProps:
name: 'radio1'
props:
label: 'Label'
---
::
### Required
Use the `required` prop to display a red star next to the label.
::component-card
---
baseProps:
name: 'radio2'
props:
label: 'Label'
required: true
---
::
### Help
Use the `help` prop to display some text under the Radio.
::component-card
---
baseProps:
name: 'radio3'
props:
label: 'Label'
help: 'Please choose one'
---
::
### Disabled
Use the `disabled` prop to disable the Radio.
::component-card
---
baseProps:
name: 'radio4'
value: true
props:
disabled: true
---
::
## Props

View File

@@ -57,7 +57,7 @@ You can put a `CommandPalette` anywhere you want but it's most commonly used ins
#code
```vue
<script setup>
const open = ref(false)
const isOpen = ref(false)
const people = [
{ id: 1, label: 'Wade Cooper' },
@@ -77,9 +77,9 @@ const selected = ref([])
<template>
<div>
<UButton label="Open" @click="open = true" />
<UButton label="Open" @click="isOpen = true" />
<UModal v-model="open">
<UModal v-model="isOpen">
<UCommandPalette
v-model="selected"
multiple
@@ -159,6 +159,8 @@ function onSelect (option) {
Use any icon from [Iconify](https://icones.js.org) by setting the `icon` prop by using this pattern: `i-{collection_name}-{icon_name}`.
Use the `selectedIcon` prop to set a different icon or change it globally in `ui.commandPalette.default.selectedIcon`. Defaults to `i-heroicons-check-20-solid`.
::component-card
---
padding: false
@@ -230,6 +232,35 @@ excludedProps:
---
::
## Full-text search
The CommandPalette component takes care of the full-text search for you with [Fuse.js](https://fusejs.io). You can pass all the options of Fuse.js through the `fuse` prop.
When searching for a command, the component will look for a `label` property on the command by default. You can customize this behaviour by overriding the `commandAttribute` prop. This will also affect the display of the command.
You can also highlight the matches in the command by setting the `fuse.fuseOptions.includeMatches` to `true`. The CommandPalette component automatically takes care of the highlighting for you.
```vue
<template>
<UCommandPalette
command-attribute="title"
:fuse="{
fuseOptions: {
ignoreLocation: true,
includeMatches: true,
threshold: 0,
keys: ['title', 'description', 'children.children.value', 'children.children.children.value']
},
resultLimit: 10
}"
/>
</template>
```
::alert{icon="i-heroicons-light-bulb"}
Try it yourself in this documentation's search by pressing :badge-shortcut{value="meta"} :badge-shortcut{value="K" class="ml-1"}.
::
## Themes
Our theming system provides a lot of flexibility to customize the component. Here is some examples of what you can do.

View File

@@ -14,14 +14,14 @@ headlessui:
#code
```vue
<script setup>
const open = ref(false)
const isOpen = ref(false)
</script>
<template>
<div>
<UButton label="Open" @click="open = true" />
<UButton label="Open" @click="isOpen = true" />
<UModal v-model="open">
<UModal v-model="isOpen">
<!-- Content -->
</UModal>
</div>
@@ -38,14 +38,14 @@ You can put a [Card](/layout/card) component inside your Modal to handle forms a
#code
```vue
<script setup>
const open = ref(false)
const isOpen = ref(false)
</script>
<template>
<div>
<UButton label="Open" @click="open = true" />
<UButton label="Open" @click="isOpen = true" />
<UModal v-model="open">
<UModal v-model="isOpen">
<UCard :ui="{ divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
<template #header>
<!-- Content -->

View File

@@ -13,14 +13,14 @@ headlessui:
#code
```vue
<script setup>
const open = ref(false)
const isOpen = ref(false)
</script>
<template>
<div>
<UButton label="Open" @click="open = true" />
<UButton label="Open" @click="isOpen = true" />
<USlideover v-model="open">
<USlideover v-model="isOpen">
<!-- Content -->
</USlideover>
</div>

View File

@@ -11,12 +11,13 @@ github: true
```vue
<script setup>
const { x, y } = useMouse()
const { y: windowY } = useWindowScroll()
const isOpen = ref(false)
const virtualElement = ref({ getBoundingClientRect: () => ({}) })
function openContextMenu () {
const top = unref(y)
function onContextMenu () {
const top = unref(y) - unref(windowY)
const left = unref(x)
virtualElement.value.getBoundingClientRect = () => ({
@@ -31,7 +32,7 @@ function openContextMenu () {
</script>
<template>
<div @contextmenu.prevent="openContextMenu">
<div @contextmenu.prevent="onContextMenu">
<UContextMenu v-model="isOpen" :virtual-element="virtualElement">
<!-- Content -->
</UContextMenu>

View File

@@ -0,0 +1,33 @@
---
github: true
---
## Usage
Use to show a placeholder while content is loading.
::component-example
#default
:skeleton-example
#code
```vue
<template>
<div class="flex items-center space-x-4">
<USkeleton class="h-12 w-12 rounded-full" />
<div class="space-y-2">
<USkeleton class="h-4 w-[250px]" />
<USkeleton class="h-4 w-[200px]" />
</div>
</div>
</template>
```
::
## Props
:component-props
## Preset
:component-preset

View File

@@ -3,5 +3,5 @@
</template>
<script setup lang="ts">
await navigateTo('/getting-started/installation')
await navigateTo('/getting-started')
</script>

View File

@@ -2,7 +2,6 @@ import type { Config } from 'tailwindcss'
import defaultTheme from 'tailwindcss/defaultTheme'
export default <Partial<Config>> {
darkMode: 'class',
content: [
'docs/content/**/*.md'
],

View File

@@ -1,6 +1,6 @@
{
"name": "@nuxthq/ui",
"version": "2.0.1",
"version": "2.0.4",
"repository": "https://github.com/nuxtlabs/ui",
"license": "MIT",
"exports": {

View File

@@ -316,7 +316,7 @@ const inputGroup = {
label: 'block text-sm font-medium text-gray-700 dark:text-gray-200',
labelWrapper: 'flex content-center justify-between',
container: 'mt-1 relative',
required: 'text-red-400',
required: 'text-red-500 dark:text-red-400 ml-0.5',
description: 'text-sm leading-5 text-gray-500 dark:text-gray-400',
hint: 'text-sm leading-5 text-gray-500 dark:text-gray-400',
help: 'mt-2 text-sm text-gray-500 dark:text-gray-400'
@@ -340,7 +340,7 @@ const select = {
}
const selectMenu = {
wrapper: 'relative',
wrapper: 'relative inline-flex',
container: 'z-20',
width: 'w-full',
height: 'max-h-60',
@@ -390,8 +390,7 @@ const selectMenu = {
placement: 'bottom-end'
},
default: {
selectedIcon: 'i-heroicons-check-20-solid',
trailingIcon: 'i-heroicons-chevron-down-20-solid'
selectedIcon: 'i-heroicons-check-20-solid'
}
}
@@ -399,7 +398,7 @@ const radio = {
wrapper: 'relative flex items-start',
base: 'h-4 w-4 text-primary-500 dark:text-primary-400 focus-visible:ring-2 focus-visible:ring-offset-2 bg-white dark:bg-gray-900 dark:checked:bg-current dark:checked:border-transparent focus-visible:ring-primary-500 dark:focus-visible:ring-primary-400 focus:ring-offset-white dark:focus:ring-offset-gray-900 border-gray-300 dark:border-gray-700 disabled:opacity-50 disabled:cursor-not-allowed focus:ring-0 focus:ring-transparent',
label: 'font-medium text-gray-700 dark:text-gray-200',
required: 'text-red-400',
required: 'text-red-500 dark:text-red-400',
help: 'text-gray-500 dark:text-gray-400'
}
@@ -458,6 +457,12 @@ const container = {
constrained: 'max-w-7xl'
}
const skeleton = {
base: 'animate-pulse',
background: 'bg-gray-100 dark:bg-gray-800',
rounded: 'rounded-md'
}
// Navigation
const verticalNavigation = {
@@ -739,6 +744,7 @@ export default {
toggle,
card,
container,
skeleton,
verticalNavigation,
commandPalette,
modal,

View File

@@ -1,6 +1,6 @@
<template>
<kbd :class="[ui.base, ui.size[size], ui.padding, ui.rounded, ui.font, ui.background, ui.ring]">
<slot />
<slot>{{ value }}</slot>
</kbd>
</template>
@@ -17,6 +17,10 @@ import appConfig from '#build/app.config'
export default defineComponent({
props: {
value: {
type: String,
default: null
},
size: {
type: String,
default: () => appConfig.ui.kbd.default.size,

View File

@@ -1,5 +1,9 @@
<template>
<button v-if="!$attrs.to" v-bind="$attrs" :class="inactiveClass">
<slot />
</button>
<NuxtLink
v-else
v-slot="{ href, navigate, exact, isActive, isExactActive }"
custom
>

View File

@@ -11,7 +11,14 @@
:class="ui.wrapper"
@update:model-value="onUpdate"
>
<input :value="modelValue" :required="required" class="absolute inset-0 w-px opacity-0 cursor-default" tabindex="-1" aria-hidden="true">
<input
v-if="required"
:value="modelValue"
:required="required"
class="absolute inset-0 w-px opacity-0 cursor-default"
tabindex="-1"
aria-hidden="true"
>
<component
:is="searchable ? 'ComboboxButton' : 'ListboxButton'"
@@ -161,7 +168,7 @@ export default defineComponent({
},
trailingIcon: {
type: String,
default: () => appConfig.ui.selectMenu.default.trailingIcon
default: () => appConfig.ui.select.default.trailingIcon
},
selectedIcon: {
type: String,

View File

@@ -0,0 +1,35 @@
<template>
<div :class="[ui.base, ui.background, ui.rounded]" />
</template>
<script lang="ts">
import { computed, defineComponent } from 'vue'
import type { PropType } from 'vue'
import { defu } from 'defu'
import { useAppConfig } from '#imports'
// TODO: Remove
// @ts-expect-error
import appConfig from '#build/app.config'
// const appConfig = useAppConfig()
export default defineComponent({
props: {
ui: {
type: Object as PropType<Partial<typeof appConfig.ui.skeleton>>,
default: () => appConfig.ui.skeleton
}
},
setup (props) {
// TODO: Remove
const appConfig = useAppConfig()
const ui = computed<Partial<typeof appConfig.ui.skeleton>>(() => defu({}, props.ui, appConfig.ui.skeleton))
return {
// eslint-disable-next-line vue/no-dupe-keys
ui
}
}
})
</script>