mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-15 12:39:35 +01:00
Compare commits
53 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17a96416f0 | ||
|
|
4378268117 | ||
|
|
622aef5ffe | ||
|
|
174af36000 | ||
|
|
2d64b50559 | ||
|
|
272c19de70 | ||
|
|
6a1142b403 | ||
|
|
9937951fb7 | ||
|
|
002129c299 | ||
|
|
0c2f655a27 | ||
|
|
fb16735dec | ||
|
|
aa6e523780 | ||
|
|
bfd15c1818 | ||
|
|
027d85402b | ||
|
|
99b9467dc2 | ||
|
|
70bf4a7392 | ||
|
|
b50fbcf760 | ||
|
|
b74bf9f666 | ||
|
|
c0feca136a | ||
|
|
0a4a9e3d2c | ||
|
|
0b29dd4ca5 | ||
|
|
9cce4456d0 | ||
|
|
ca4f06a313 | ||
|
|
7dd9ee528e | ||
|
|
cdf6ff7152 | ||
|
|
9c2104d947 | ||
|
|
d1c8026a1e | ||
|
|
14efa81986 | ||
|
|
b3314dc16b | ||
|
|
06135f38ae | ||
|
|
dbbab8ded0 | ||
|
|
6e77f1d514 | ||
|
|
4b6e80e364 | ||
|
|
8a1b112727 | ||
|
|
961f0ae27b | ||
|
|
e819656a34 | ||
|
|
024e03acc9 | ||
|
|
462d7729c9 | ||
|
|
df857fd541 | ||
|
|
9b208bf297 | ||
|
|
5af9da4d3c | ||
|
|
1d995136a5 | ||
|
|
ba15add4db | ||
|
|
0aca478c57 | ||
|
|
0ee4f2b75b | ||
|
|
6e53cb6281 | ||
|
|
de715304dc | ||
|
|
81854112e3 | ||
|
|
87526b9ec5 | ||
|
|
f79187825f | ||
|
|
0443ac2c9d | ||
|
|
d2c51e3667 | ||
|
|
d15d7fa01d |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,3 +9,4 @@ dist
|
||||
.vercel
|
||||
.idea
|
||||
.env
|
||||
.data
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"git": {
|
||||
"commitMessage": "chore(release): ${version}"
|
||||
"commitMessage": "chore(release): ${version}",
|
||||
"tagName": "v${version}"
|
||||
},
|
||||
"npm": {
|
||||
"publish": false
|
||||
@@ -11,7 +12,7 @@
|
||||
"web": true
|
||||
},
|
||||
"hooks": {
|
||||
"before:init": ["pnpm lint"]
|
||||
"before:init": ["pnpm lint", "pnpm typecheck"]
|
||||
},
|
||||
"plugins": {
|
||||
"@release-it/conventional-changelog": {
|
||||
|
||||
25
CHANGELOG.md
25
CHANGELOG.md
@@ -1,5 +1,30 @@
|
||||
# Changelog
|
||||
|
||||
## [2.14.2](https://github.com/nuxt/ui/compare/v2.14.1...v2.14.2) (2024-03-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Alert:** improve `description` alignment when no title provided ([ca4f06a](https://github.com/nuxt/ui/commit/ca4f06a313314af5813007878a9b6c8f1003c6d1)), closes [#1408](https://github.com/nuxt/ui/issues/1408)
|
||||
* **Checkbox:** label interaction without `FormGroup` ([#1427](https://github.com/nuxt/ui/issues/1427)) ([6e77f1d](https://github.com/nuxt/ui/commit/6e77f1d5144d7d87b0c76b43ecf3d731166c808b))
|
||||
* **Dropdown:** active/inactive dropdown links ([#1407](https://github.com/nuxt/ui/issues/1407)) ([6a1142b](https://github.com/nuxt/ui/commit/6a1142b4032150def78c69080df455f7d2a25e7b))
|
||||
* **Dropdown:** improve `hover` mode on touch devices ([70bf4a7](https://github.com/nuxt/ui/commit/70bf4a73921f47fcd41599874b595a6eed947f5a))
|
||||
* **HorizontalNavigation:** add `relative` class to icon ([0a4a9e3](https://github.com/nuxt/ui/commit/0a4a9e3d2c4a7584570e4ab7ae6fe8265c960a33))
|
||||
* **Modal:** remove `overflow-hidden` ([#1460](https://github.com/nuxt/ui/issues/1460)) ([002129c](https://github.com/nuxt/ui/commit/002129c29926df5a816288b916194ab28cf4c8a4))
|
||||
* **Notification:** improve `description` alignment when no title provided ([9cce445](https://github.com/nuxt/ui/commit/9cce4456d03c52daca4d7347e60cbcd7f501317a))
|
||||
* **Popover:** improve `hover` mode on touch devices ([b50fbcf](https://github.com/nuxt/ui/commit/b50fbcf760e908579e81f6e57234f2080e2bf035))
|
||||
* **RadioGroup:** add missing `fieldset` config ([2d64b50](https://github.com/nuxt/ui/commit/2d64b50559946b166c02cfe921e63d746cdc09d4)), closes [#1472](https://github.com/nuxt/ui/issues/1472)
|
||||
* **SelectMenu:** check `null` model value ([4b6e80e](https://github.com/nuxt/ui/commit/4b6e80e3646e263a83614830d9ec6adb0edf2ede)), closes [#1421](https://github.com/nuxt/ui/issues/1421)
|
||||
* **Tooltip:** arrow not hidden on mobile ([272c19d](https://github.com/nuxt/ui/commit/272c19de708144b1b132b98a7287254974f4e144)), closes [#1470](https://github.com/nuxt/ui/issues/1470)
|
||||
* **VerticalNavigation:** add `relative` class to icon ([0b29dd4](https://github.com/nuxt/ui/commit/0b29dd4ca560cac7d151132e086eab17c0498a5c)), closes [#1245](https://github.com/nuxt/ui/issues/1245)
|
||||
|
||||
## [2.14.1](https://github.com/nuxt/ui/compare/v2.14.0...v2.14.1) (2024-02-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **module:** revert tailwind config from [#1272](https://github.com/nuxt/ui/issues/1272) ([#1404](https://github.com/nuxt/ui/issues/1404)) ([ba15add](https://github.com/nuxt/ui/commit/ba15add4db5d2f84e987819628cbbf88edcbad57))
|
||||
|
||||
## [2.14.0](https://github.com/nuxt/ui/compare/v2.13.0...v2.14.0) (2024-02-22)
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
<div>
|
||||
<NuxtLoadingIndicator />
|
||||
|
||||
<Banner v-if="!$route.path.startsWith('/examples')" />
|
||||
|
||||
<Header v-if="!$route.path.startsWith('/examples')" :links="links" />
|
||||
|
||||
<NuxtLayout>
|
||||
@@ -12,7 +14,7 @@
|
||||
<Footer v-if="!$route.path.startsWith('/examples')" />
|
||||
|
||||
<ClientOnly>
|
||||
<LazyUDocsSearch ref="searchRef" :files="files" :navigation="navigation" :links="links" :fuse="{ resultLimit: 1000 }" />
|
||||
<LazyUContentSearch ref="searchRef" :files="files" :navigation="navigation" :links="links" :fuse="{ resultLimit: 42 }" />
|
||||
</ClientOnly>
|
||||
|
||||
<UNotifications>
|
||||
|
||||
58
docs/components/Banner.vue
Normal file
58
docs/components/Banner.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<script setup lang="ts">
|
||||
const id = 'nuxt-ui-banner-1'
|
||||
const to = '/pro/pricing'
|
||||
|
||||
const hideBanner = () => {
|
||||
localStorage.setItem(id, 'true')
|
||||
|
||||
document.querySelector('html')?.classList.add('hide-banner')
|
||||
}
|
||||
|
||||
if (process.server) {
|
||||
useHead({
|
||||
script: [{
|
||||
key: 'prehydrate-template-banner',
|
||||
innerHTML: `
|
||||
if (localStorage.getItem('${id}') === 'true') {
|
||||
document.querySelector('html').classList.add('hide-banner')
|
||||
}`.replace(/\s+/g, ' '),
|
||||
type: 'text/javascript'
|
||||
}]
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative bg-primary hover:bg-primary/90 transition-[background] backdrop-blur z-50 app-banner">
|
||||
<UContainer class="py-2">
|
||||
<NuxtLink v-if="to" :to="to" class="focus:outline-none" aria-label="Nuxt UI Pro pricing" tabindex="-1">
|
||||
<span class="absolute inset-0 " aria-hidden="true" />
|
||||
</NuxtLink>
|
||||
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<div class="lg:flex-1 hidden lg:flex items-center" />
|
||||
|
||||
<p class="text-sm font-medium text-white dark:text-gray-900">
|
||||
<UIcon name="i-heroicons-rocket-launch" class="w-5 h-5 align-top flex-shrink-0 pointer-events-none mr-2" />
|
||||
<span class="font-semibold">Nuxt UI Pro v1.0</span> is out with dashboard components!
|
||||
</p>
|
||||
|
||||
<div class="flex items-center justify-end lg:flex-1">
|
||||
<button
|
||||
class="p-1.5 rounded-md inline-flex hover:bg-primary/90"
|
||||
aria-label="Close banner"
|
||||
@click.prevent="hideBanner"
|
||||
>
|
||||
<UIcon name="i-heroicons-x-mark-20-solid" class="w-5 h-5 text-white dark:text-gray-900" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</UContainer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.hide-banner .app-banner {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@@ -18,8 +18,8 @@
|
||||
<template #right>
|
||||
<ColorPicker />
|
||||
|
||||
<UTooltip text="Search" :shortcuts="[metaSymbol, 'K']">
|
||||
<UDocsSearchButton :label="null" />
|
||||
<UTooltip text="Search" :shortcuts="[metaSymbol, 'K']" :popper="{ strategy: 'absolute' }">
|
||||
<UContentSearchButton :label="null" />
|
||||
</UTooltip>
|
||||
|
||||
<UColorModeButton />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<UPopover mode="hover">
|
||||
<UPopover mode="hover" :popper="{ strategy: 'absolute' }" :ui="{ width: 'w-[156px]' }">
|
||||
<template #default="{ open }">
|
||||
<UButton color="gray" variant="ghost" square :class="[open && 'bg-gray-50 dark:bg-gray-800']" aria-label="Color picker">
|
||||
<UIcon name="i-heroicons-swatch-20-solid" class="w-5 h-5 text-primary-500 dark:text-primary-400" />
|
||||
|
||||
@@ -27,8 +27,8 @@ function selectRange (duration: Duration) {
|
||||
</UButton>
|
||||
|
||||
<template #panel="{ close }">
|
||||
<div class="flex items-center divide-x divide-gray-200 dark:divide-gray-800">
|
||||
<div class="flex flex-col py-4">
|
||||
<div class="flex items-center sm:divide-x divide-gray-200 dark:divide-gray-800">
|
||||
<div class="hidden sm:flex flex-col py-4">
|
||||
<UButton
|
||||
v-for="(range, index) in ranges"
|
||||
:key="index"
|
||||
@@ -37,6 +37,7 @@ function selectRange (duration: Duration) {
|
||||
variant="ghost"
|
||||
class="rounded-none px-6"
|
||||
:class="[isRangeSelected(range.duration) ? 'bg-gray-100 dark:bg-gray-800' : 'hover:bg-gray-50 dark:hover:bg-gray-800/50']"
|
||||
truncate
|
||||
@click="selectRange(range.duration)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -3,5 +3,5 @@ const value = ref(50)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<URange v-model="value" name="range" />
|
||||
<URange v-model="value" />
|
||||
</template>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { useBreakpoints, breakpointsTailwind } from '@vueuse/core'
|
||||
import { DatePicker as VCalendarDatePicker } from 'v-calendar'
|
||||
// @ts-ignore
|
||||
import type { DatePickerDate, DatePickerRangeObject } from 'v-calendar/dist/types/src/use/datePicker'
|
||||
@@ -25,6 +26,10 @@ const date = computed({
|
||||
}
|
||||
})
|
||||
|
||||
const breakpoints = useBreakpoints(breakpointsTailwind)
|
||||
|
||||
const smallerThanSm = breakpoints.smaller('sm')
|
||||
|
||||
const attrs = {
|
||||
transparent: true,
|
||||
borderless: true,
|
||||
@@ -35,7 +40,7 @@ const attrs = {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VCalendarDatePicker v-if="date && (date as DatePickerRangeObject)?.start && (date as DatePickerRangeObject)?.end" v-model.range="date" :columns="2" v-bind="{ ...attrs, ...$attrs }" />
|
||||
<VCalendarDatePicker v-if="date && (date as DatePickerRangeObject)?.start && (date as DatePickerRangeObject)?.end" v-model.range="date" :columns="smallerThanSm ? 1 : 2" :rows="smallerThanSm ? 2 : 1" v-bind="{ ...attrs, ...$attrs }" />
|
||||
<VCalendarDatePicker v-else v-model="date" v-bind="{ ...attrs, ...$attrs }" />
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
description: Display togglable accordion panels.
|
||||
links:
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/elements/Accordion.vue
|
||||
- label: Disclosure
|
||||
icon: i-simple-icons-headlessui
|
||||
to: 'https://headlessui.com/vue/disclosure'
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/elements/Accordion.vue
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
---
|
||||
title: Breadcrumb
|
||||
description: A list of links that indicate the current page's location within a navigational hierarchy.
|
||||
links:
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/navigation/Breadcrumb.vue
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
title: CommandPalette
|
||||
description: Add a customizable command palette to your app.
|
||||
links:
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/navigation/CommandPalette.vue
|
||||
- label: 'Combobox'
|
||||
icon: i-simple-icons-headlessui
|
||||
to: 'https://headlessui.com/vue/combobox'
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/navigation/CommandPalette.vue
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -16,15 +16,15 @@ Let's start by installing the `v-calendar` and `date-fns` dependency:
|
||||
::code-group
|
||||
|
||||
```bash [pnpm]
|
||||
pnpm add v-calendar@next
|
||||
pnpm add v-calendar@next date-fns
|
||||
```
|
||||
|
||||
```bash [yarn]
|
||||
yarn add v-calendar@next
|
||||
yarn add v-calendar@next date-fns
|
||||
```
|
||||
|
||||
```bash [npm]
|
||||
npm install v-calendar@next
|
||||
npm install v-calendar@next date-fns
|
||||
```
|
||||
|
||||
::
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
description: Display a list of actions in a dropdown menu.
|
||||
links:
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/elements/Dropdown.vue
|
||||
- label: Menu
|
||||
icon: i-simple-icons-headlessui
|
||||
to: https://headlessui.com/vue/menu
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/elements/Dropdown.vue
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -236,7 +236,7 @@ Use the `#help` slot to set the custom content for help.
|
||||
::component-card
|
||||
---
|
||||
slots:
|
||||
help: Here are some examples <UIcon name="i-heroicons-arrow-right-20-solid" />
|
||||
help: Here are some examples <UIcon name="i-heroicons-information-circle" />
|
||||
default: <UInput model-value="benjamincanac" placeholder="you@example.com" />
|
||||
props:
|
||||
label: 'Email'
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
title: InputMenu
|
||||
description: Display an autocomplete input with real-time suggestions.
|
||||
links:
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/InputMenu.vue
|
||||
- label: 'Combobox'
|
||||
icon: i-simple-icons-headlessui
|
||||
to: 'https://headlessui.com/vue/combobox'
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/InputMenu.vue
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -33,6 +33,18 @@ It also renders an `<a>` tag when a `to` prop is provided, otherwise it defaults
|
||||
|
||||
It is used underneath by the [Button](/components/button), [Dropdown](/components/dropdown) and [VerticalNavigation](/components/vertical-navigation) components.
|
||||
|
||||
## IntelliSense
|
||||
If you're using VSCode and wish to get autocompletion for the classes `active-class` and `inactive-class`, you can add the following settings to your `.vscode/settings.json`:
|
||||
|
||||
```json [.vscode/settings.json]
|
||||
{
|
||||
"tailwindCSS.classAttributes": [
|
||||
"active-class",
|
||||
"inactive-class"
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
:component-props
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
description: Display a modal within your application.
|
||||
links:
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/overlays/Modal.vue
|
||||
- label: 'Dialog'
|
||||
icon: i-simple-icons-headlessui
|
||||
to: 'https://headlessui.com/vue/dialog'
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/overlays/Modal.vue
|
||||
---
|
||||
|
||||
## Usage
|
||||
@@ -79,6 +79,8 @@ Then, you can use the `useModal` composable to control your modals within your a
|
||||
|
||||
:component-example{component="modal-example-composable"}
|
||||
|
||||
Additionally, you can close the modal within the modal component by calling `modal.close()`.
|
||||
|
||||
## Props
|
||||
|
||||
:component-props
|
||||
|
||||
@@ -289,8 +289,14 @@ Slots defined in the `<UNotifications />` component are automatically passed dow
|
||||
|
||||
## Props
|
||||
|
||||
:component-props
|
||||
::tabs
|
||||
:component-props{label="Notification"}
|
||||
:component-props{label="Notifications" slug="UNotifications"}
|
||||
::
|
||||
|
||||
## Config
|
||||
|
||||
:component-preset
|
||||
::tabs
|
||||
:component-preset{label="Notification"}
|
||||
:component-preset{label="Notifications" slug="Notifications"}
|
||||
::
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
description: Display a non-modal dialog that floats around a trigger element.
|
||||
links:
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/overlays/Popover.vue
|
||||
- label: 'Popover'
|
||||
icon: i-simple-icons-headlessui
|
||||
to: 'https://headlessui.com/vue/popover'
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/overlays/Popover.vue
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
title: SelectMenu
|
||||
description: Display a select menu with advanced features.
|
||||
links:
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/SelectMenu.vue
|
||||
- label: 'Listbox'
|
||||
icon: i-simple-icons-headlessui
|
||||
to: 'https://headlessui.com/vue/listbox'
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/SelectMenu.vue
|
||||
---
|
||||
|
||||
## Usage
|
||||
@@ -257,7 +257,7 @@ componentProps:
|
||||
Use the `#option-create` slot to customize the content displayed when the `creatable` prop is `true` and there is no options. You will have access to the `query` property in the slot scope.
|
||||
|
||||
::callout{icon="i-heroicons-light-bulb"}
|
||||
An example is available in the [Create option](#create-option) section.
|
||||
An example is available in the [Creatable](#creatable) section.
|
||||
::
|
||||
|
||||
### `empty`
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
description: Display a dialog that slides in from the edge of the screen.
|
||||
links:
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/overlays/Slideover.vue
|
||||
- label: 'Dialog'
|
||||
icon: i-simple-icons-headlessui
|
||||
to: 'https://headlessui.com/vue/dialog'
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/overlays/Slideover.vue
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
description: Display a toggle field.
|
||||
links:
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/Toggle.vue
|
||||
- label: 'Switch'
|
||||
icon: i-simple-icons-headlessui
|
||||
to: 'https://headlessui.com/vue/switch'
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/Toggle.vue
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -79,10 +79,10 @@ pro:
|
||||
title: Upgrade to <span class="text-primary">Nuxt UI Pro</span>
|
||||
description: 'Nuxt UI Pro is a collection of premium Vue components built on top of Nuxt UI to create beautiful & responsive Nuxt applications in minutes.<br>It includes all primitives to build landing pages, documentations, blogs, dashboards or entire SaaS products.'
|
||||
links:
|
||||
- label: View plans
|
||||
- label: Buy now
|
||||
to: /pro/pricing
|
||||
color: gray
|
||||
icon: i-heroicons-credit-card
|
||||
color: black
|
||||
trailingIcon: i-heroicons-arrow-right-20-solid
|
||||
size: lg
|
||||
- label: Explore templates
|
||||
to: /pro/templates
|
||||
@@ -178,7 +178,7 @@ pro:
|
||||
</UPageBody>
|
||||
|
||||
<template #right>
|
||||
<UDocsToc :links="page.body.toc.links" />
|
||||
<UContentToc :links="page.body.toc.links" />
|
||||
</template>
|
||||
</UPage>
|
||||
</template>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<Footer />
|
||||
|
||||
<ClientOnly>
|
||||
<LazyUDocsSearch :files="files" :navigation="navigation" :links="links" :fuse="{ resultLimit: 1000 }" />
|
||||
<LazyUContentSearch :files="files" :navigation="navigation" :links="links" :fuse="{ resultLimit: 42 }" />
|
||||
</ClientOnly>
|
||||
|
||||
<UNotifications>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createResolver } from '@nuxt/kit'
|
||||
import colors from 'tailwindcss/colors'
|
||||
import module from '../src/module'
|
||||
import { excludeColors } from '../src/runtime/utils/colors'
|
||||
import { excludeColors } from '../src/colors'
|
||||
import pkg from '../package.json'
|
||||
|
||||
const { resolve } = createResolver(import.meta.url)
|
||||
@@ -17,11 +17,10 @@ export default defineNuxtConfig({
|
||||
].filter(Boolean),
|
||||
modules: [
|
||||
'@nuxt/content',
|
||||
'@nuxt/fonts',
|
||||
'@nuxt/image',
|
||||
'nuxt-og-image',
|
||||
module,
|
||||
'@nuxtjs/fontaine',
|
||||
'@nuxtjs/google-fonts',
|
||||
'@nuxtjs/plausible',
|
||||
'@vueuse/nuxt',
|
||||
'nuxt-component-meta',
|
||||
|
||||
@@ -7,21 +7,20 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify-json/heroicons": "^1.1.20",
|
||||
"@iconify-json/simple-icons": "^1.1.92",
|
||||
"@iconify-json/simple-icons": "^1.1.94",
|
||||
"@nuxt/content": "^2.12.0",
|
||||
"@nuxt/devtools": "^1.0.8",
|
||||
"@nuxt/eslint-config": "^0.2.0",
|
||||
"@nuxt/fonts": "^0.0.1",
|
||||
"@nuxt/image": "^1.3.0",
|
||||
"@nuxt/ui-pro": "npm:@nuxt/ui-pro-edge@0.7.5-28475621.09eb8fa",
|
||||
"@nuxtjs/fontaine": "^0.4.1",
|
||||
"@nuxtjs/google-fonts": "^3.1.3",
|
||||
"@nuxt/ui-pro": "npm:@nuxt/ui-pro-edge@1.0.1-28492961.4d49b9c",
|
||||
"@nuxtjs/plausible": "^0.2.4",
|
||||
"@octokit/rest": "^20.0.2",
|
||||
"@vueuse/nuxt": "^10.8.0",
|
||||
"@vueuse/nuxt": "^10.9.0",
|
||||
"date-fns": "^3.3.1",
|
||||
"eslint": "^8.56.0",
|
||||
"joi": "^17.12.1",
|
||||
"nuxt": "^3.10.2",
|
||||
"eslint": "^8.57.0",
|
||||
"joi": "^17.12.2",
|
||||
"nuxt": "^3.10.3",
|
||||
"nuxt-cloudflare-analytics": "^1.0.8",
|
||||
"nuxt-component-meta": "^0.6.3",
|
||||
"nuxt-og-image": "^2.2.4",
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
|
||||
<hr v-if="surround?.length">
|
||||
|
||||
<UDocsSurround :surround="surround" />
|
||||
<UContentSurround :surround="surround" />
|
||||
</UPageBody>
|
||||
|
||||
<template v-if="page?.body?.toc?.links?.length" #right>
|
||||
<UDocsToc :links="page.body.toc.links">
|
||||
<UContentToc :links="page.body.toc.links">
|
||||
<template #bottom>
|
||||
<div class="hidden lg:block space-y-6 !mt-6">
|
||||
<UDivider v-if="page.body?.toc?.links?.length" type="dashed" />
|
||||
@@ -30,7 +30,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</UDocsToc>
|
||||
</UContentToc>
|
||||
</template>
|
||||
</UPage>
|
||||
</template>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<ULandingHero :ui="{ base: 'relative z-[1]', container: 'max-w-4xl' }" class="mb-[calc(var(--header-height)*2)]">
|
||||
<template #headline>
|
||||
<UBadge variant="subtle" size="md" class="hover:bg-primary-100 dark:bg-primary-950/100 dark:hover:bg-primary-900 transition-color relative font-medium rounded-full shadow-none">
|
||||
<NuxtLink :to="`https://github.com/nuxt/ui/releases/tag/v${config.version.split('.').slice(0, -1).join('.')}.0`" target="_blank" class="focus:outline-none" tabindex="-1">
|
||||
<NuxtLink :to="`https://github.com/nuxt/ui/releases/tag/v${config.version.split('.').slice(0, -1).join('.')}.0`" target="_blank" class="focus:outline-none" aria-label="Go to last relase" tabindex="-1">
|
||||
<span class="absolute inset-0" aria-hidden="true" />
|
||||
</NuxtLink>
|
||||
|
||||
@@ -90,6 +90,7 @@
|
||||
:width="card.image.width"
|
||||
:height="card.image.height"
|
||||
:alt="card.title"
|
||||
loading="lazy"
|
||||
class="object-cover w-full"
|
||||
/>
|
||||
</ULandingCard>
|
||||
@@ -164,7 +165,13 @@
|
||||
</ULandingSection>
|
||||
|
||||
<template v-if="navigation.find(item => item._path === '/pro')">
|
||||
<ULandingHero id="pro" :links="page.pro.links" :ui="{ title: 'sm:text-6xl' }">
|
||||
<div class="relative">
|
||||
<UDivider class="absolute inset-x-0" />
|
||||
|
||||
<div class="w-full relative overflow-hidden h-px bg-gradient-to-r from-gray-800 via-primary-400 to-gray-800 max-w-5xl mx-auto" />
|
||||
</div>
|
||||
|
||||
<ULandingHero id="pro" :links="page.pro.links" :ui="{ title: 'sm:text-6xl' }" class="bg-gradient-to-b from-gray-50 dark:from-gray-950/50 to-white dark:to-gray-900 relative">
|
||||
<template #title>
|
||||
<span v-html="page.pro.title" />
|
||||
</template>
|
||||
@@ -173,11 +180,13 @@
|
||||
<span v-html="page.pro.description" />
|
||||
</template>
|
||||
|
||||
<video poster="https://res.cloudinary.com/nuxt/video/upload/so_14.8/v1708511800/ui-pro/video-nuxt-ui-pro_kwfbdh.jpg" controls class="rounded-lg">
|
||||
<source src="https://res.cloudinary.com/nuxt/video/upload/v1708511800/ui-pro/video-nuxt-ui-pro_kwfbdh.webm" type="video/webm">
|
||||
<source src="https://res.cloudinary.com/nuxt/video/upload/v1708511800/ui-pro/video-nuxt-ui-pro_kwfbdh.mp4" type="video/mp4">
|
||||
<source src="https://res.cloudinary.com/nuxt/video/upload/v1708511800/ui-pro/video-nuxt-ui-pro_kwfbdh.ogg" type="video/ogg">
|
||||
</video>
|
||||
<div class="bg-gray-900/5 dark:bg-white/5 ring-1 ring-inset ring-gray-900/10 dark:ring-white/10 rounded-xl lg:-m-4 p-4">
|
||||
<video preload="none" poster="https://res.cloudinary.com/nuxt/video/upload/so_3.3/v1708511800/ui-pro/video-nuxt-ui-pro_kwfbdh.jpg" controls class="rounded-lg">
|
||||
<source src="https://res.cloudinary.com/nuxt/video/upload/v1708511800/ui-pro/video-nuxt-ui-pro_kwfbdh.webm" type="video/webm">
|
||||
<source src="https://res.cloudinary.com/nuxt/video/upload/v1708511800/ui-pro/video-nuxt-ui-pro_kwfbdh.mp4" type="video/mp4">
|
||||
<source src="https://res.cloudinary.com/nuxt/video/upload/v1708511800/ui-pro/video-nuxt-ui-pro_kwfbdh.ogg" type="video/ogg">
|
||||
</video>
|
||||
</div>
|
||||
</ULandingHero>
|
||||
|
||||
<ULandingSection v-for="(section, index) in page.pro.sections" :key="index" v-bind="section" class="!pt-0">
|
||||
@@ -246,7 +255,7 @@
|
||||
</template>
|
||||
|
||||
<template #aside-top>
|
||||
<UDocsSearchButton size="md" class="w-full" />
|
||||
<UContentSearchButton size="md" class="w-full" />
|
||||
</template>
|
||||
|
||||
<template #aside-default>
|
||||
@@ -274,8 +283,8 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #docs-surround>
|
||||
<UDocsSurround
|
||||
<template #content-surround>
|
||||
<UContentSurround
|
||||
:surround="(surround as unknown as ParsedContent[])"
|
||||
class="w-full gap-4"
|
||||
:ui="{
|
||||
@@ -292,9 +301,9 @@
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #docs-toc>
|
||||
<template #content-toc>
|
||||
<div class="absolute top-0 left-0 right-0 space-y-3">
|
||||
<UDocsToc :links="toc" class="bg-transparent relative max-h-full overflow-hidden top-0" :ui="({ container: { base: '!pt-0 !pb-4' } } as any)" />
|
||||
<UContentToc :links="toc" class="bg-transparent relative max-h-full overflow-hidden top-0" :ui="({ container: { base: '!pt-0 !pb-4' } } as any)" />
|
||||
|
||||
<UDivider type="dashed" :ui="{ border: { base: 'border-gray-800/10 dark:border-gray-200/10' } }" />
|
||||
|
||||
@@ -372,7 +381,7 @@
|
||||
<template #description>
|
||||
<span v-html="page.pro.landing?.description" />
|
||||
</template>
|
||||
<video poster="https://res.cloudinary.com/nuxt/video/upload/so_14.4/v1698923423/ui-pro/nuxt-ui-pro-landing-demo_yrh6nr.jpg" controls>
|
||||
<video preload="none" poster="https://res.cloudinary.com/nuxt/video/upload/so_14.4/v1698923423/ui-pro/nuxt-ui-pro-landing-demo_yrh6nr.jpg" controls>
|
||||
<source src="https://res.cloudinary.com/nuxt/video/upload/v1698923423/ui-pro/nuxt-ui-pro-landing-demo_yrh6nr.webm" type="video/webm">
|
||||
<source src="https://res.cloudinary.com/nuxt/video/upload/v1698923423/ui-pro/nuxt-ui-pro-landing-demo_yrh6nr.mp4" type="video/mp4">
|
||||
<source src="https://res.cloudinary.com/nuxt/video/upload/v1698923423/ui-pro/nuxt-ui-pro-landing-demo_yrh6nr.ogg" type="video/ogg">
|
||||
@@ -385,7 +394,7 @@
|
||||
<template #description>
|
||||
<span v-html="page.pro.docs?.description" />
|
||||
</template>
|
||||
<video poster="https://res.cloudinary.com/nuxt/video/upload/v1698923398/ui-pro/nuxt-ui-pro-docs-demo_jm6ubr.jpg" controls>
|
||||
<video preload="none" poster="https://res.cloudinary.com/nuxt/video/upload/v1698923398/ui-pro/nuxt-ui-pro-docs-demo_jm6ubr.jpg" controls>
|
||||
<source src="https://res.cloudinary.com/nuxt/video/upload/v1698923398/ui-pro/nuxt-ui-pro-docs-demo_jm6ubr.webm" type="video/webm">
|
||||
<source src="https://res.cloudinary.com/nuxt/video/upload/v1698923398/ui-pro/nuxt-ui-pro-docs-demo_jm6ubr.mp4" type="video/mp4">
|
||||
<source src="https://res.cloudinary.com/nuxt/video/upload/v1698923398/ui-pro/nuxt-ui-pro-docs-demo_jm6ubr.ogg" type="video/ogg">
|
||||
@@ -609,11 +618,11 @@ const docsBlocks = computed(() => [isAfterStep(steps.docs) && {
|
||||
slot: 'page-body',
|
||||
class: 'inset-x-4 top-4 justify-start'
|
||||
}, isAfterStep(steps.docs + 12) ? {
|
||||
slot: 'docs-surround',
|
||||
slot: 'content-surround',
|
||||
class: 'bottom-4 inset-x-4 h-28'
|
||||
} : {
|
||||
name: 'UDocsSurround',
|
||||
to: '/pro/components/docs-surround',
|
||||
name: 'UContentSurround',
|
||||
to: '/pro/components/content-surround',
|
||||
class: 'bottom-4 inset-x-4 h-28',
|
||||
inactive: false
|
||||
}]
|
||||
@@ -621,12 +630,12 @@ const docsBlocks = computed(() => [isAfterStep(steps.docs) && {
|
||||
name: '#default',
|
||||
class: 'left-4 right-72 inset-y-4'
|
||||
}]), isAfterStep(steps.docs + 13) ? {
|
||||
name: 'UDocsToc',
|
||||
to: '/pro/components/docs-toc',
|
||||
name: 'UContentToc',
|
||||
to: '/pro/components/content-toc',
|
||||
class: 'right-4 inset-y-4 w-64',
|
||||
inactive: isAfterStep(steps.docs + 14),
|
||||
children: [{
|
||||
slot: 'docs-toc',
|
||||
slot: 'content-toc',
|
||||
class: 'inset-4 overflow-y-auto'
|
||||
}]
|
||||
} : {
|
||||
@@ -755,10 +764,12 @@ const navigationLinks = [{
|
||||
|
||||
const surround = [{
|
||||
title: 'Introduction',
|
||||
description: 'Fully styled and customizable components for Nuxt.'
|
||||
description: 'Fully styled and customizable components for Nuxt.',
|
||||
_path: '/'
|
||||
}, {
|
||||
title: 'Theming',
|
||||
description: 'Learn how to customize the look and feel of the components.'
|
||||
description: 'Learn how to customize the look and feel of the components.',
|
||||
_path: '/'
|
||||
}]
|
||||
|
||||
const md = `
|
||||
|
||||
@@ -5,7 +5,7 @@ export default <Partial<Config>>{
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ['DM Sans', 'DM Sans fallback', ...defaultTheme.fontFamily.sans]
|
||||
sans: ['DM Sans', ...defaultTheme.fontFamily.sans]
|
||||
},
|
||||
colors: {
|
||||
green: {
|
||||
|
||||
28
package.json
28
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nuxt/ui",
|
||||
"version": "2.14.0",
|
||||
"version": "2.14.2",
|
||||
"repository": "nuxt/ui",
|
||||
"homepage": "https://ui.nuxt.com",
|
||||
"type": "module",
|
||||
@@ -37,7 +37,7 @@
|
||||
"@headlessui/tailwindcss": "^0.2.0",
|
||||
"@headlessui/vue": "^1.7.19",
|
||||
"@iconify-json/heroicons": "^1.1.20",
|
||||
"@nuxt/kit": "^3.10.2",
|
||||
"@nuxt/kit": "^3.10.3",
|
||||
"@nuxtjs/color-mode": "^3.3.2",
|
||||
"@nuxtjs/tailwindcss": "^6.11.4",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
@@ -45,9 +45,9 @@
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
"@tailwindcss/forms": "^0.5.7",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@vueuse/core": "^10.8.0",
|
||||
"@vueuse/integrations": "^10.8.0",
|
||||
"@vueuse/math": "^10.8.0",
|
||||
"@vueuse/core": "^10.9.0",
|
||||
"@vueuse/integrations": "^10.9.0",
|
||||
"@vueuse/math": "^10.9.0",
|
||||
"defu": "^6.1.4",
|
||||
"fuse.js": "^6.6.2",
|
||||
"nuxt-icon": "^0.6.8",
|
||||
@@ -63,25 +63,25 @@
|
||||
"@nuxt/test-utils": "^3.11.0",
|
||||
"@release-it/conventional-changelog": "^8.0.1",
|
||||
"@vue/test-utils": "^2.4.4",
|
||||
"eslint": "^8.56.0",
|
||||
"happy-dom": "^13.3.8",
|
||||
"joi": "^17.12.1",
|
||||
"nuxt": "^3.10.2",
|
||||
"eslint": "^8.57.0",
|
||||
"happy-dom": "^13.6.2",
|
||||
"joi": "^17.12.2",
|
||||
"nuxt": "^3.10.3",
|
||||
"release-it": "^17.1.1",
|
||||
"typescript": "^5.3.3",
|
||||
"unbuild": "^2.0.0",
|
||||
"valibot": "^0.29.0",
|
||||
"vitest": "^1.3.1",
|
||||
"vitest-environment-nuxt": "^1.0.0",
|
||||
"vue-tsc": "^1.8.27",
|
||||
"vue-tsc": "^2.0.4",
|
||||
"yup": "^1.3.3",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
"resolutions": {
|
||||
"@nuxt/kit": "^3.10.2",
|
||||
"@nuxt/schema": "3.10.2",
|
||||
"@nuxt/kit": "3.10.3",
|
||||
"@nuxt/schema": "3.10.3",
|
||||
"tailwindcss": "3.4.1",
|
||||
"@headlessui/vue": "^1.7.19",
|
||||
"vue": "3.4.19"
|
||||
"@headlessui/vue": "1.7.19",
|
||||
"vue": "3.4.21"
|
||||
}
|
||||
}
|
||||
|
||||
1359
pnpm-lock.yaml
generated
1359
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": [
|
||||
"@nuxtjs"
|
||||
"github>nuxt/renovate-config-nuxt"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { omit } from './lodash'
|
||||
import { omit } from './runtime/utils/lodash'
|
||||
import { kebabCase, camelCase, upperFirst } from 'scule'
|
||||
|
||||
const colorsToExclude = [
|
||||
112
src/module.ts
112
src/module.ts
@@ -1,10 +1,22 @@
|
||||
import { createRequire } from 'node:module'
|
||||
import { defineNuxtModule, installModule, addComponentsDir, addImportsDir, createResolver, addPlugin } from '@nuxt/kit'
|
||||
import type { CollectionNames, IconsPluginOptions } from '@egoist/tailwindcss-icons'
|
||||
import { defaultExtractor as createDefaultExtractor } from 'tailwindcss/lib/lib/defaultExtractor.js'
|
||||
import { iconsPlugin, getIconCollections, type CollectionNames, type IconsPluginOptions } from '@egoist/tailwindcss-icons'
|
||||
import { name, version } from '../package.json'
|
||||
import createTemplates from './templates'
|
||||
import { generateSafelist, excludeColors, customSafelistExtractor } from './colors'
|
||||
import * as config from './runtime/ui.config'
|
||||
import type { DeepPartial, Strategy } from './runtime/types/utils'
|
||||
import installTailwind from './tailwind'
|
||||
|
||||
const defaultExtractor = createDefaultExtractor({ tailwindConfig: { separator: ':' } })
|
||||
const _require = createRequire(import.meta.url)
|
||||
const defaultColors = _require('tailwindcss/colors.js')
|
||||
|
||||
delete defaultColors.lightBlue
|
||||
delete defaultColors.warmGray
|
||||
delete defaultColors.trueGray
|
||||
delete defaultColors.coolGray
|
||||
delete defaultColors.blueGray
|
||||
|
||||
type UI = {
|
||||
primary?: string
|
||||
@@ -76,13 +88,107 @@ export default defineNuxtModule<ModuleOptions>({
|
||||
nuxt.options.css.push(resolve(runtimeDir, 'ui.css'))
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
nuxt.hook('tailwindcss:config', function (tailwindConfig) {
|
||||
tailwindConfig.theme = tailwindConfig.theme || {}
|
||||
tailwindConfig.theme.extend = tailwindConfig.theme.extend || {}
|
||||
tailwindConfig.theme.extend.colors = tailwindConfig.theme.extend.colors || {}
|
||||
|
||||
const globalColors: any = {
|
||||
...(tailwindConfig.theme.colors || defaultColors),
|
||||
...tailwindConfig.theme.extend?.colors
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
globalColors.primary = tailwindConfig.theme.extend.colors.primary = {
|
||||
50: 'rgb(var(--color-primary-50) / <alpha-value>)',
|
||||
100: 'rgb(var(--color-primary-100) / <alpha-value>)',
|
||||
200: 'rgb(var(--color-primary-200) / <alpha-value>)',
|
||||
300: 'rgb(var(--color-primary-300) / <alpha-value>)',
|
||||
400: 'rgb(var(--color-primary-400) / <alpha-value>)',
|
||||
500: 'rgb(var(--color-primary-500) / <alpha-value>)',
|
||||
600: 'rgb(var(--color-primary-600) / <alpha-value>)',
|
||||
700: 'rgb(var(--color-primary-700) / <alpha-value>)',
|
||||
800: 'rgb(var(--color-primary-800) / <alpha-value>)',
|
||||
900: 'rgb(var(--color-primary-900) / <alpha-value>)',
|
||||
950: 'rgb(var(--color-primary-950) / <alpha-value>)',
|
||||
DEFAULT: 'rgb(var(--color-primary-DEFAULT) / <alpha-value>)'
|
||||
}
|
||||
|
||||
if (globalColors.gray) {
|
||||
// @ts-ignore
|
||||
globalColors.cool = tailwindConfig.theme.extend.colors.cool = defaultColors.gray
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
globalColors.gray = tailwindConfig.theme.extend.colors.gray = {
|
||||
50: 'rgb(var(--color-gray-50) / <alpha-value>)',
|
||||
100: 'rgb(var(--color-gray-100) / <alpha-value>)',
|
||||
200: 'rgb(var(--color-gray-200) / <alpha-value>)',
|
||||
300: 'rgb(var(--color-gray-300) / <alpha-value>)',
|
||||
400: 'rgb(var(--color-gray-400) / <alpha-value>)',
|
||||
500: 'rgb(var(--color-gray-500) / <alpha-value>)',
|
||||
600: 'rgb(var(--color-gray-600) / <alpha-value>)',
|
||||
700: 'rgb(var(--color-gray-700) / <alpha-value>)',
|
||||
800: 'rgb(var(--color-gray-800) / <alpha-value>)',
|
||||
900: 'rgb(var(--color-gray-900) / <alpha-value>)',
|
||||
950: 'rgb(var(--color-gray-950) / <alpha-value>)'
|
||||
}
|
||||
|
||||
const colors = excludeColors(globalColors)
|
||||
|
||||
// @ts-ignore
|
||||
nuxt.options.appConfig.ui = {
|
||||
primary: 'green',
|
||||
gray: 'cool',
|
||||
colors,
|
||||
strategy: 'merge'
|
||||
}
|
||||
|
||||
tailwindConfig.safelist = tailwindConfig.safelist || []
|
||||
tailwindConfig.safelist.push(...generateSafelist(options.safelistColors || [], colors))
|
||||
})
|
||||
|
||||
createTemplates(nuxt)
|
||||
|
||||
// Modules
|
||||
|
||||
await installModule('nuxt-icon')
|
||||
await installModule('@nuxtjs/color-mode', { classSuffix: '' })
|
||||
await installTailwind(options, nuxt, { resolve, runtimeDir })
|
||||
await installModule('@nuxtjs/tailwindcss', {
|
||||
exposeConfig: true,
|
||||
config: {
|
||||
darkMode: 'class',
|
||||
plugins: [
|
||||
require('@tailwindcss/forms')({ strategy: 'class' }),
|
||||
require('@tailwindcss/aspect-ratio'),
|
||||
require('@tailwindcss/typography'),
|
||||
require('@tailwindcss/container-queries'),
|
||||
require('@headlessui/tailwindcss'),
|
||||
iconsPlugin(Array.isArray(options.icons) || options.icons === 'all' ? { collections: getIconCollections(options.icons) } : typeof options.icons === 'object' ? options.icons as IconsPluginOptions : {})
|
||||
],
|
||||
content: {
|
||||
files: [
|
||||
resolve(runtimeDir, 'components/**/*.{vue,mjs,ts}'),
|
||||
resolve(runtimeDir, 'ui.config/**/*.{mjs,js,ts}')
|
||||
],
|
||||
transform: {
|
||||
vue: (content) => {
|
||||
return content.replaceAll(/(?:\r\n|\r|\n)/g, ' ')
|
||||
}
|
||||
},
|
||||
extract: {
|
||||
vue: (content) => {
|
||||
return [
|
||||
...defaultExtractor(content),
|
||||
// @ts-ignore
|
||||
...customSafelistExtractor(options.prefix, content, nuxt.options.appConfig.ui.colors, options.safelistColors)
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Plugins
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
{{ title }}
|
||||
</slot>
|
||||
</p>
|
||||
<p v-if="description || $slots.description" :class="ui.description">
|
||||
<p v-if="description || $slots.description" :class="twMerge(ui.description, !(title && $slots.title) && 'mt-0 leading-5')">
|
||||
<slot name="description" :description="description">
|
||||
{{ description }}
|
||||
</slot>
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
:disabled="disabled"
|
||||
:class="ui.trigger"
|
||||
role="button"
|
||||
@mouseover="onMouseOver"
|
||||
@mouseenter="onMouseEnter"
|
||||
@touchstart.prevent="onTouchStart"
|
||||
>
|
||||
<slot :open="open" :disabled="disabled">
|
||||
<button :disabled="disabled">
|
||||
@@ -16,25 +17,25 @@
|
||||
</slot>
|
||||
</HMenuButton>
|
||||
|
||||
<div v-if="open && items.length" ref="container" :class="[ui.container, ui.width]" :style="containerStyle" @mouseover="onMouseOver">
|
||||
<div v-if="open && items.length" ref="container" :class="[ui.container, ui.width]" :style="containerStyle">
|
||||
<Transition appear v-bind="ui.transition">
|
||||
<div>
|
||||
<div v-if="popper.arrow" data-popper-arrow :class="Object.values(ui.arrow)" />
|
||||
|
||||
<HMenuItems :class="[ui.base, ui.divide, ui.ring, ui.rounded, ui.shadow, ui.background, ui.height]" static>
|
||||
<div v-for="(subItems, index) of items" :key="index" :class="ui.padding">
|
||||
<NuxtLink v-for="(item, subIndex) of subItems" :key="subIndex" v-slot="{ href, target, rel, navigate, isExternal }" v-bind="getNuxtLinkProps(item)" custom>
|
||||
<NuxtLink v-for="(item, subIndex) of subItems" :key="subIndex" v-slot="{ href, target, rel, navigate, isExternal, isActive }" v-bind="getNuxtLinkProps(item)" custom>
|
||||
<HMenuItem v-slot="{ active, disabled: itemDisabled, close }" :disabled="item.disabled">
|
||||
<component
|
||||
:is="!!href ? 'a' : 'button'"
|
||||
:href="!itemDisabled ? href : undefined"
|
||||
:rel="rel"
|
||||
:target="target"
|
||||
:class="twMerge(twJoin(ui.item.base, ui.item.padding, ui.item.size, ui.item.rounded, active ? ui.item.active : ui.item.inactive, itemDisabled && ui.item.disabled), item.class)"
|
||||
:class="twMerge(twJoin(ui.item.base, ui.item.padding, ui.item.size, ui.item.rounded, active || isActive ? ui.item.active : ui.item.inactive, itemDisabled && ui.item.disabled), item.class)"
|
||||
@click="onClick($event, item, { href, navigate, close, isExternal })"
|
||||
>
|
||||
<slot :name="item.slot || 'item'" :item="item">
|
||||
<UIcon v-if="item.icon" :name="item.icon" :class="twMerge(twJoin(ui.item.icon.base, active ? ui.item.icon.active : ui.item.icon.inactive), item.iconClass)" />
|
||||
<UIcon v-if="item.icon" :name="item.icon" :class="twMerge(twJoin(ui.item.icon.base, active || isActive ? ui.item.icon.active : ui.item.icon.inactive), item.iconClass)" />
|
||||
<UAvatar v-else-if="item.avatar" v-bind="{ size: ui.item.avatar.size, ...item.avatar }" :class="ui.item.avatar.base" />
|
||||
|
||||
<span :class="twMerge(ui.item.label, item.labelClass)">{{ item.label }}</span>
|
||||
@@ -181,7 +182,19 @@ export default defineComponent({
|
||||
}
|
||||
})
|
||||
|
||||
function onMouseOver () {
|
||||
function onTouchStart () {
|
||||
if (!menuApi.value) {
|
||||
return
|
||||
}
|
||||
|
||||
if (menuApi.value.menuState === 0) {
|
||||
menuApi.value.closeMenu()
|
||||
} else {
|
||||
menuApi.value.openMenu()
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseEnter () {
|
||||
if (props.mode !== 'hover' || !menuApi.value) {
|
||||
return
|
||||
}
|
||||
@@ -263,7 +276,8 @@ export default defineComponent({
|
||||
trigger,
|
||||
container,
|
||||
containerStyle,
|
||||
onMouseOver,
|
||||
onTouchStart,
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
onClick,
|
||||
getNuxtLinkProps,
|
||||
|
||||
@@ -39,6 +39,7 @@ import type { Strategy } from '../../types'
|
||||
import appConfig from '#build/app.config'
|
||||
import { checkbox } from '#ui/ui.config'
|
||||
import colors from '#ui-colors'
|
||||
import { useId } from '#app'
|
||||
|
||||
const config = mergeConfig<typeof checkbox>(appConfig.ui.strategy, appConfig.ui.checkbox, checkbox)
|
||||
|
||||
@@ -105,7 +106,8 @@ export default defineComponent({
|
||||
setup (props, { emit }) {
|
||||
const { ui, attrs } = useUI('checkbox', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||
|
||||
const { emitFormChange, color, name, inputId } = useFormGroup(props)
|
||||
const { emitFormChange, color, name, inputId: _inputId } = useFormGroup(props)
|
||||
const inputId = _inputId.value ?? useId()
|
||||
|
||||
const toggle = computed({
|
||||
get () {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div :class="ui.wrapper">
|
||||
<fieldset v-bind="attrs">
|
||||
<fieldset v-bind="attrs" :class="ui.fieldset">
|
||||
<legend v-if="legend || $slots.legend" :class="ui.legend">
|
||||
<slot name="legend">
|
||||
{{ legend }}
|
||||
|
||||
@@ -362,7 +362,7 @@ export default defineComponent({
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
} else {
|
||||
} else if (props.modelValue) {
|
||||
if (props.valueAttribute) {
|
||||
const option = props.options.find(option => option[props.valueAttribute] === props.modelValue)
|
||||
return option ? option[props.optionAttribute] : null
|
||||
@@ -370,6 +370,8 @@ export default defineComponent({
|
||||
return ['string', 'number'].includes(typeof props.modelValue) ? props.modelValue : props.modelValue[props.optionAttribute]
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
const selectClass = computed(() => {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div :class="ui.group.wrapper" role="option">
|
||||
<div :class="ui.group.wrapper">
|
||||
<h2 v-if="label" :class="ui.group.label">
|
||||
{{ label }}
|
||||
</h2>
|
||||
|
||||
<div :class="ui.group.container" role="listbox" :aria-label="group[groupAttribute]">
|
||||
<div :class="ui.group.container" :aria-label="group[groupAttribute]">
|
||||
<HComboboxOption
|
||||
v-for="(command, index) of group.commands"
|
||||
:key="`${group.key}-${index}`"
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
{{ title }}
|
||||
</slot>
|
||||
</p>
|
||||
<p v-if="(description || $slots.description)" :class="ui.description">
|
||||
<p v-if="(description || $slots.description)" :class="twMerge(ui.description, !(title && $slots.title) && 'mt-0 leading-5')">
|
||||
<slot name="description" :description="description">
|
||||
{{ description }}
|
||||
</slot>
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
:disabled="disabled"
|
||||
:class="ui.trigger"
|
||||
role="button"
|
||||
@mouseover="onMouseOver"
|
||||
@mouseenter="onMouseEnter"
|
||||
@touchstart.prevent="onTouchStart"
|
||||
>
|
||||
<slot :open="open" :close="close">
|
||||
<button :disabled="disabled">
|
||||
@@ -20,7 +21,7 @@
|
||||
<div v-if="open" :class="[ui.overlay.base, ui.overlay.background]" />
|
||||
</Transition>
|
||||
|
||||
<div v-if="open" ref="container" :class="[ui.container, ui.width]" :style="containerStyle" @mouseover="onMouseOver">
|
||||
<div v-if="open" ref="container" :class="[ui.container, ui.width]" :style="containerStyle">
|
||||
<Transition appear v-bind="ui.transition">
|
||||
<div>
|
||||
<div v-if="popper.arrow" data-popper-arrow :class="Object.values(ui.arrow)" />
|
||||
@@ -153,7 +154,19 @@ export default defineComponent({
|
||||
}
|
||||
})
|
||||
|
||||
function onMouseOver () {
|
||||
function onTouchStart () {
|
||||
if (!popoverApi.value) {
|
||||
return
|
||||
}
|
||||
|
||||
if (popoverApi.value.popoverState === 0) {
|
||||
popoverApi.value.closePopover()
|
||||
} else {
|
||||
popoverApi.value.togglePopover()
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseEnter () {
|
||||
if (props.mode !== 'hover' || !popoverApi.value) {
|
||||
return
|
||||
}
|
||||
@@ -223,7 +236,8 @@ export default defineComponent({
|
||||
trigger,
|
||||
container,
|
||||
containerStyle,
|
||||
onMouseOver,
|
||||
onTouchStart,
|
||||
onMouseEnter,
|
||||
onMouseLeave
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div ref="trigger" :class="ui.wrapper" v-bind="attrs" @mouseover="onMouseOver" @mouseleave="onMouseLeave">
|
||||
<div ref="trigger" :class="ui.wrapper" v-bind="attrs" @mouseenter="onMouseEnter" @mouseleave="onMouseLeave">
|
||||
<slot :open="open">
|
||||
Hover
|
||||
</slot>
|
||||
@@ -96,7 +96,7 @@ export default defineComponent({
|
||||
|
||||
// Methods
|
||||
|
||||
function onMouseOver () {
|
||||
function onMouseEnter () {
|
||||
// cancel programmed closing
|
||||
if (closeTimeout) {
|
||||
clearTimeout(closeTimeout)
|
||||
@@ -137,7 +137,7 @@ export default defineComponent({
|
||||
trigger,
|
||||
container,
|
||||
open,
|
||||
onMouseOver,
|
||||
onMouseEnter,
|
||||
onMouseLeave
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
export default {
|
||||
wrapper: 'relative flex items-start',
|
||||
fieldset: '',
|
||||
legend: 'text-sm font-medium text-gray-700 dark:text-gray-200 mb-1',
|
||||
default: {
|
||||
color: 'primary'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ export default {
|
||||
inactive: 'text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white',
|
||||
label: 'truncate relative',
|
||||
icon: {
|
||||
base: 'flex-shrink-0 w-5 h-5',
|
||||
base: 'flex-shrink-0 w-5 h-5 relative',
|
||||
active: 'text-gray-700 dark:text-gray-200',
|
||||
inactive: 'text-gray-400 dark:text-gray-500 group-hover:text-gray-700 dark:group-hover:text-gray-200'
|
||||
},
|
||||
|
||||
@@ -11,7 +11,7 @@ export default {
|
||||
inactive: 'text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:before:bg-gray-50 dark:hover:before:bg-gray-800/50',
|
||||
label: 'truncate relative',
|
||||
icon: {
|
||||
base: 'flex-shrink-0 w-5 h-5',
|
||||
base: 'flex-shrink-0 w-5 h-5 relative',
|
||||
active: 'text-gray-700 dark:text-gray-200',
|
||||
inactive: 'text-gray-400 dark:text-gray-500 group-hover:text-gray-700 dark:group-hover:text-gray-200'
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@ export default {
|
||||
container: 'flex min-h-full items-end sm:items-center justify-center text-center',
|
||||
padding: 'p-4 sm:p-0',
|
||||
margin: 'sm:my-8',
|
||||
base: 'relative text-left rtl:text-right overflow-hidden flex flex-col',
|
||||
base: 'relative text-left rtl:text-right flex flex-col',
|
||||
overlay: {
|
||||
base: 'fixed inset-0 transition-opacity',
|
||||
background: 'bg-gray-200/75 dark:bg-gray-800/75',
|
||||
|
||||
@@ -28,5 +28,8 @@ export default {
|
||||
openDelay: 0,
|
||||
closeDelay: 0
|
||||
},
|
||||
arrow
|
||||
arrow: {
|
||||
...arrow,
|
||||
base: '[@media(pointer:coarse)]:hidden invisible before:visible before:block before:rotate-45 before:z-[-1] before:w-2 before:h-2'
|
||||
}
|
||||
}
|
||||
|
||||
135
src/tailwind.ts
135
src/tailwind.ts
@@ -1,135 +0,0 @@
|
||||
import { createRequire } from 'node:module'
|
||||
import { join } from 'pathe'
|
||||
import { addTemplate, installModule, useNuxt } from '@nuxt/kit'
|
||||
import { excludeColors, generateSafelist } from './runtime/utils/colors'
|
||||
import type { ModuleOptions } from './module'
|
||||
|
||||
const _require = createRequire(import.meta.url)
|
||||
const defaultColors = _require('tailwindcss/colors.js')
|
||||
|
||||
delete defaultColors.lightBlue
|
||||
delete defaultColors.warmGray
|
||||
delete defaultColors.trueGray
|
||||
delete defaultColors.coolGray
|
||||
delete defaultColors.blueGray
|
||||
|
||||
export default async function installTailwind (moduleOptions: ModuleOptions, nuxt = useNuxt(), { resolve, runtimeDir }) {
|
||||
// 1. register hook
|
||||
// @ts-ignore
|
||||
nuxt.hook('tailwindcss:config', function (tailwindConfig) {
|
||||
tailwindConfig.theme = tailwindConfig.theme || {}
|
||||
tailwindConfig.theme.extend = tailwindConfig.theme.extend || {}
|
||||
tailwindConfig.theme.extend.colors =
|
||||
tailwindConfig.theme.extend.colors || {}
|
||||
|
||||
const globalColors: any = {
|
||||
...(tailwindConfig.theme.colors || defaultColors),
|
||||
...tailwindConfig.theme.extend?.colors
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
globalColors.primary = tailwindConfig.theme.extend.colors.primary = {
|
||||
50: 'rgb(var(--color-primary-50) / <alpha-value>)',
|
||||
100: 'rgb(var(--color-primary-100) / <alpha-value>)',
|
||||
200: 'rgb(var(--color-primary-200) / <alpha-value>)',
|
||||
300: 'rgb(var(--color-primary-300) / <alpha-value>)',
|
||||
400: 'rgb(var(--color-primary-400) / <alpha-value>)',
|
||||
500: 'rgb(var(--color-primary-500) / <alpha-value>)',
|
||||
600: 'rgb(var(--color-primary-600) / <alpha-value>)',
|
||||
700: 'rgb(var(--color-primary-700) / <alpha-value>)',
|
||||
800: 'rgb(var(--color-primary-800) / <alpha-value>)',
|
||||
900: 'rgb(var(--color-primary-900) / <alpha-value>)',
|
||||
950: 'rgb(var(--color-primary-950) / <alpha-value>)',
|
||||
DEFAULT: 'rgb(var(--color-primary-DEFAULT) / <alpha-value>)'
|
||||
}
|
||||
|
||||
if (globalColors.gray) {
|
||||
// @ts-ignore
|
||||
globalColors.cool = tailwindConfig.theme.extend.colors.cool =
|
||||
defaultColors.gray
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
globalColors.gray = tailwindConfig.theme.extend.colors.gray = {
|
||||
50: 'rgb(var(--color-gray-50) / <alpha-value>)',
|
||||
100: 'rgb(var(--color-gray-100) / <alpha-value>)',
|
||||
200: 'rgb(var(--color-gray-200) / <alpha-value>)',
|
||||
300: 'rgb(var(--color-gray-300) / <alpha-value>)',
|
||||
400: 'rgb(var(--color-gray-400) / <alpha-value>)',
|
||||
500: 'rgb(var(--color-gray-500) / <alpha-value>)',
|
||||
600: 'rgb(var(--color-gray-600) / <alpha-value>)',
|
||||
700: 'rgb(var(--color-gray-700) / <alpha-value>)',
|
||||
800: 'rgb(var(--color-gray-800) / <alpha-value>)',
|
||||
900: 'rgb(var(--color-gray-900) / <alpha-value>)',
|
||||
950: 'rgb(var(--color-gray-950) / <alpha-value>)'
|
||||
}
|
||||
|
||||
const colors = excludeColors(globalColors)
|
||||
|
||||
// @ts-ignore
|
||||
nuxt.options.appConfig.ui = {
|
||||
primary: 'green',
|
||||
gray: 'cool',
|
||||
colors,
|
||||
strategy: 'merge'
|
||||
}
|
||||
|
||||
tailwindConfig.safelist = tailwindConfig.safelist || []
|
||||
tailwindConfig.safelist.push(
|
||||
...generateSafelist(moduleOptions.safelistColors || [], colors)
|
||||
)
|
||||
})
|
||||
|
||||
// 2. add config template
|
||||
const configTemplate = addTemplate({
|
||||
filename: 'nuxtui-tailwind.config.cjs',
|
||||
write: true,
|
||||
getContents: () => `
|
||||
const { defaultExtractor: createDefaultExtractor } = require('tailwindcss/lib/lib/defaultExtractor.js')
|
||||
const { customSafelistExtractor } = require(${JSON.stringify(resolve(runtimeDir, 'utils', 'colors'))})
|
||||
const { iconsPlugin, getIconCollections } = require('@egoist/tailwindcss-icons')
|
||||
|
||||
const defaultExtractor = createDefaultExtractor({ tailwindConfig: { separator: ':' } })
|
||||
|
||||
module.exports = {
|
||||
darkMode: 'class',
|
||||
plugins: [
|
||||
require('@tailwindcss/forms')({ strategy: 'class' }),
|
||||
require('@tailwindcss/aspect-ratio'),
|
||||
require('@tailwindcss/typography'),
|
||||
require('@tailwindcss/container-queries'),
|
||||
require('@headlessui/tailwindcss'),
|
||||
iconsPlugin(${Array.isArray(moduleOptions.icons) || moduleOptions.icons === 'all' ? `{ collections: getIconCollections(${JSON.stringify(moduleOptions.icons)}) }` : typeof moduleOptions.icons === 'object' ? JSON.stringify(moduleOptions.icons) : '{}'})
|
||||
],
|
||||
content: {
|
||||
files: [
|
||||
${JSON.stringify(resolve(runtimeDir, 'components/**/*.{vue,mjs,ts}'))},
|
||||
${JSON.stringify(resolve(runtimeDir, 'ui.config/**/*.{mjs,js,ts}'))}
|
||||
],
|
||||
transform: {
|
||||
vue: (content) => {
|
||||
return content.replaceAll(/(?:\\r\\n|\\r|\\n)/g, ' ')
|
||||
}
|
||||
},
|
||||
extract: {
|
||||
vue: (content) => {
|
||||
return [
|
||||
...defaultExtractor(content),
|
||||
...customSafelistExtractor(${JSON.stringify(moduleOptions.prefix)}, content, ${JSON.stringify(nuxt.options.appConfig.ui.colors)}, ${JSON.stringify(moduleOptions.safelistColors)})
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
})
|
||||
|
||||
// 3. install module
|
||||
await installModule('@nuxtjs/tailwindcss', {
|
||||
exposeConfig: true,
|
||||
configPath: [
|
||||
configTemplate.dst,
|
||||
join(nuxt.options.rootDir, 'tailwind.config')
|
||||
]
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user