mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-21 15:31:46 +01:00
feat(module)!: use tailwind-merge for class merging (#509)
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<UTooltip :text="color.value" class="capitalize" :open-delay="500">
|
||||
<UButton
|
||||
color="gray"
|
||||
color="transparent"
|
||||
square
|
||||
:ui="{
|
||||
color: {
|
||||
gray: {
|
||||
transparent: {
|
||||
solid: 'bg-gray-100 dark:bg-gray-800',
|
||||
ghost: 'hover:bg-gray-50 dark:hover:bg-gray-800/50'
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
v-model="componentProps[prop.name]"
|
||||
:name="`prop-${prop.name}`"
|
||||
tabindex="-1"
|
||||
:ui="{ wrapper: 'relative flex items-start justify-center' }"
|
||||
class="justify-center"
|
||||
/>
|
||||
<USelectMenu
|
||||
v-else-if="prop.type === 'string' && prop.options.length"
|
||||
@@ -16,8 +16,9 @@
|
||||
:options="prop.options"
|
||||
:name="`prop-${prop.name}`"
|
||||
variant="none"
|
||||
:ui-menu="{ width: 'w-32 !-mt-px', rounded: 'rounded-b-md', wrapper: 'relative inline-flex' }"
|
||||
class="!py-0"
|
||||
class="inline-flex"
|
||||
:ui-menu="{ width: 'w-32 !-mt-px', rounded: 'rounded-t-none' }"
|
||||
select-class="py-0"
|
||||
tabindex="-1"
|
||||
:popper="{ strategy: 'fixed', placement: 'bottom-start' }"
|
||||
/>
|
||||
@@ -28,7 +29,7 @@
|
||||
:name="`prop-${prop.name}`"
|
||||
variant="none"
|
||||
autocomplete="off"
|
||||
class="!py-0"
|
||||
input-class="py-0"
|
||||
tabindex="-1"
|
||||
@update:model-value="val => componentProps[prop.name] = prop.type === 'number' ? Number(val) : val"
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<UFormGroup v-slot="{ error }" label="Email" :error="!email && 'You must enter an email'" help="This is a nice email!">
|
||||
<UInput v-model="email" type="email" placeholder="Enter email" :trailing-icon="error && 'i-heroicons-exclamation-triangle-20-solid'" />
|
||||
<UInput v-model="email" type="email" placeholder="Enter email" :trailing-icon="error ? 'i-heroicons-exclamation-triangle-20-solid' : undefined" />
|
||||
</UFormGroup>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -231,7 +231,7 @@ const { data: todos, pending } = await useLazyAsyncData('todos', () => $fetch<{
|
||||
:total="pageTotal"
|
||||
:ui="{
|
||||
wrapper: 'flex items-center gap-1',
|
||||
rounded: 'rounded-full min-w-[32px] justify-center',
|
||||
rounded: '!rounded-full min-w-[32px] justify-center',
|
||||
default: {
|
||||
activeButton: {
|
||||
variant: 'outline'
|
||||
|
||||
@@ -9,7 +9,7 @@ const items = ref(Array(55))
|
||||
:total="items.length"
|
||||
:ui="{
|
||||
wrapper: 'flex items-center gap-1',
|
||||
rounded: 'rounded-full min-w-[32px] justify-center'
|
||||
rounded: '!rounded-full min-w-[32px] justify-center'
|
||||
}"
|
||||
:prev-button="null"
|
||||
:next-button="{
|
||||
|
||||
@@ -25,8 +25,8 @@ const links = [{
|
||||
:links="links"
|
||||
:ui="{
|
||||
wrapper: 'border-s border-gray-200 dark:border-gray-800 space-y-2',
|
||||
base: 'group block border-s -ms-px lg:leading-6',
|
||||
padding: 'ps-4',
|
||||
base: 'group block border-s -ms-px lg:leading-6 before:hidden',
|
||||
padding: 'p-0 ps-4',
|
||||
rounded: '',
|
||||
font: '',
|
||||
ring: '',
|
||||
|
||||
@@ -77,6 +77,8 @@ This can also happen when you bind a dynamic color to a component: `<UBadge :col
|
||||
|
||||
## Components
|
||||
|
||||
### `app.config.ts`
|
||||
|
||||
Components are styled with Tailwind CSS 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 those in your own `app.config.ts`.
|
||||
|
||||
```ts [app.config.ts]
|
||||
@@ -89,6 +91,8 @@ export default defineAppConfig({
|
||||
})
|
||||
```
|
||||
|
||||
### `ui` prop
|
||||
|
||||
Each component has a `ui` prop that allows you to customize everything specifically.
|
||||
|
||||
```vue
|
||||
@@ -103,6 +107,45 @@ Each component has a `ui` prop that allows you to customize everything specifica
|
||||
You can find the default classes for each component under the `Preset` section.
|
||||
::
|
||||
|
||||
Thanks to [tailwind-merge](https://github.com/dcastil/tailwind-merge), the `ui` prop is smartly merged with the config. This means you don't have to rewrite everything. :u-badge{label="Edge" class="!rounded-full" variant="subtle"}
|
||||
|
||||
For example, the default preset of the `FormGroup` component looks like this:
|
||||
|
||||
```json
|
||||
{
|
||||
...
|
||||
"label": {
|
||||
"base": "block font-medium text-gray-700 dark:text-gray-200",
|
||||
...
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
To change the font of the `label`, you only need to write:
|
||||
|
||||
```vue
|
||||
<UFormGroup name="email" label="Email" :ui="{ label: { base: 'font-semibold' } }">
|
||||
...
|
||||
</UFormGroup>
|
||||
```
|
||||
|
||||
This will smartly replace the `font-medium` by `font-semibold` and prevent any class duplication and any class priority issue.
|
||||
|
||||
### `class` attribute
|
||||
|
||||
You can also use the `class` attribute to add classes to the component.
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<UButton label="Button" class="rounded-full" />
|
||||
</template>
|
||||
```
|
||||
|
||||
Again, with [tailwind-merge](https://github.com/dcastil/tailwind-merge), this will smartly merge the classes with the `ui` prop and the config. :u-badge{label="Edge" class="!rounded-full" variant="subtle"}
|
||||
|
||||
### Default values
|
||||
|
||||
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]
|
||||
|
||||
@@ -223,8 +223,8 @@ const links = [{
|
||||
:links="links"
|
||||
:ui="{
|
||||
wrapper: 'border-s border-gray-200 dark:border-gray-800 space-y-2',
|
||||
base: 'group block border-s -ms-px lg:leading-6',
|
||||
padding: 'ps-4',
|
||||
base: 'group block border-s -ms-px lg:leading-6 before:hidden',
|
||||
padding: 'p-0 ps-4',
|
||||
rounded: '',
|
||||
font: '',
|
||||
ring: '',
|
||||
@@ -254,7 +254,7 @@ const items = ref(Array(55))
|
||||
:total="items.length"
|
||||
:ui="{
|
||||
wrapper: 'flex items-center gap-1',
|
||||
rounded: 'rounded-full min-w-[32px] justify-center'
|
||||
rounded: '!rounded-full min-w-[32px] justify-center'
|
||||
}"
|
||||
:prev-button="null"
|
||||
:next-button="{
|
||||
|
||||
@@ -142,8 +142,6 @@ baseProps:
|
||||
props:
|
||||
label: 'Email'
|
||||
error: true
|
||||
ui:
|
||||
error: 'hidden'
|
||||
excludedProps:
|
||||
- ui
|
||||
- error
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"defu": "^6.1.2",
|
||||
"fuse.js": "^6.6.2",
|
||||
"lodash-es": "^4.17.21",
|
||||
"tailwind-merge": "^1.14.0",
|
||||
"tailwindcss": "^3.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -54,13 +55,13 @@
|
||||
"@nuxt/module-builder": "^0.4.0",
|
||||
"@release-it/conventional-changelog": "^7.0.0",
|
||||
"eslint": "^8.47.0",
|
||||
"joi": "^17.9.2",
|
||||
"nuxt": "^3.6.5",
|
||||
"release-it": "^16.1.4",
|
||||
"typescript": "^5.1.6",
|
||||
"unbuild": "^1.2.1",
|
||||
"vue-tsc": "^1.8.8",
|
||||
"yup": "^1.2.0",
|
||||
"joi": "^17.9.2",
|
||||
"zod": "^3.21.4"
|
||||
}
|
||||
}
|
||||
|
||||
35
pnpm-lock.yaml
generated
35
pnpm-lock.yaml
generated
@@ -1,9 +1,5 @@
|
||||
lockfileVersion: '6.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
@@ -59,6 +55,9 @@ importers:
|
||||
lodash-es:
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.21
|
||||
tailwind-merge:
|
||||
specifier: ^1.14.0
|
||||
version: 1.14.0
|
||||
tailwindcss:
|
||||
specifier: ^3.3.3
|
||||
version: 3.3.3
|
||||
@@ -111,7 +110,7 @@ importers:
|
||||
version: 1.1.12
|
||||
'@iconify-json/simple-icons':
|
||||
specifier: latest
|
||||
version: 1.1.65
|
||||
version: 1.1.63
|
||||
'@nuxt/content':
|
||||
specifier: ^2.7.2
|
||||
version: 2.7.2(rollup@3.26.2)
|
||||
@@ -1179,7 +1178,7 @@ packages:
|
||||
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
|
||||
dependencies:
|
||||
eslint: 8.47.0
|
||||
eslint-visitor-keys: 3.4.3
|
||||
eslint-visitor-keys: 3.4.1
|
||||
dev: true
|
||||
|
||||
/@eslint-community/regexpp@4.5.1:
|
||||
@@ -1267,8 +1266,8 @@ packages:
|
||||
dependencies:
|
||||
'@iconify/types': 2.0.0
|
||||
|
||||
/@iconify-json/simple-icons@1.1.65:
|
||||
resolution: {integrity: sha512-pCL5nF80e5YrvnR9VFt7EROEcWE6vvtBaThx+hC1Xaw3e1DkCTc4JioiNnYrON/p2iNVvncAQAgQP1Us/XPInw==}
|
||||
/@iconify-json/simple-icons@1.1.63:
|
||||
resolution: {integrity: sha512-aIbo99YLjwZ53XxU9HC+CO9Br9H0vRuGmq8b0TOaGA1g7HX2ZlpmzPvWAte5xbmbohfRzAMX/OVsFhOT+CL+Aw==}
|
||||
dependencies:
|
||||
'@iconify/types': 2.0.0
|
||||
dev: true
|
||||
@@ -1566,7 +1565,7 @@ packages:
|
||||
birpc: 0.2.12
|
||||
boxen: 7.1.1
|
||||
consola: 3.2.3
|
||||
error-stack-parser-es: 0.1.0
|
||||
error-stack-parser-es: 0.1.1
|
||||
execa: 7.2.0
|
||||
fast-folder-size: 2.1.0
|
||||
fast-glob: 3.3.1
|
||||
@@ -1591,7 +1590,7 @@ packages:
|
||||
sirv: 2.0.3
|
||||
unimport: 3.1.3(rollup@3.26.2)
|
||||
vite: 4.4.7(@types/node@20.4.5)
|
||||
vite-plugin-inspect: 0.7.35(@nuxt/kit@3.6.5)(rollup@3.26.2)(vite@4.4.7)
|
||||
vite-plugin-inspect: 0.7.37(@nuxt/kit@3.6.5)(rollup@3.26.2)(vite@4.4.7)
|
||||
vite-plugin-vue-inspector: 3.6.0(vite@4.4.7)
|
||||
wait-on: 7.0.1
|
||||
which: 3.0.1
|
||||
@@ -1787,7 +1786,7 @@ packages:
|
||||
consola: 3.2.3
|
||||
cssnano: 6.0.1(postcss@8.4.26)
|
||||
defu: 6.1.2
|
||||
esbuild: 0.18.17
|
||||
esbuild: 0.18.13
|
||||
escape-string-regexp: 5.0.0
|
||||
estree-walker: 3.0.3
|
||||
externality: 1.0.2
|
||||
@@ -5030,8 +5029,8 @@ packages:
|
||||
dependencies:
|
||||
is-arrayish: 0.2.1
|
||||
|
||||
/error-stack-parser-es@0.1.0:
|
||||
resolution: {integrity: sha512-K5/Oncl6ZizGM7tqGUc3Sd82zVKGsZ+l8FqhhnF8+10QujC/xT2VKwdaM/8rAR5F1BouVqgemMrhHG23vhOpMw==}
|
||||
/error-stack-parser-es@0.1.1:
|
||||
resolution: {integrity: sha512-g/9rfnvnagiNf+DRMHEVGuGuIBlCIMDFoTA616HaP2l9PlCjGjVhD98PNbVSJvmK4TttqT5mV5tInMhoFgi+aA==}
|
||||
dev: true
|
||||
|
||||
/es-abstract@1.21.2:
|
||||
@@ -6464,7 +6463,6 @@ packages:
|
||||
/iconv-lite@0.6.3:
|
||||
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
dev: true
|
||||
@@ -8270,7 +8268,7 @@ packages:
|
||||
defu: 6.1.2
|
||||
destr: 2.0.0
|
||||
dot-prop: 7.2.0
|
||||
esbuild: 0.18.17
|
||||
esbuild: 0.18.13
|
||||
escape-string-regexp: 5.0.0
|
||||
etag: 1.8.1
|
||||
fs-extra: 11.1.1
|
||||
@@ -10934,7 +10932,6 @@ packages:
|
||||
|
||||
/tailwind-merge@1.14.0:
|
||||
resolution: {integrity: sha512-3mFKyCo/MBcgyOTlrY8T7odzZFx+w+qKSMAmdFzRvqBfLlSigU6TZnlFHK0lkMwj9Bj8OYU+9yW9lmGuS0QEnQ==}
|
||||
dev: true
|
||||
|
||||
/tailwindcss@3.3.3:
|
||||
resolution: {integrity: sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==}
|
||||
@@ -11903,8 +11900,8 @@ packages:
|
||||
vue-tsc: 1.8.8(typescript@5.1.6)
|
||||
dev: true
|
||||
|
||||
/vite-plugin-inspect@0.7.35(@nuxt/kit@3.6.5)(rollup@3.26.2)(vite@4.4.7):
|
||||
resolution: {integrity: sha512-e5w5dJAj3vDcHTxn8hHbiH+mVqYs17gaW00f3aGuMTXiqUog+T1Lsxr9Jb4WRiip84cpuhR0KFFBT1egtXboiA==}
|
||||
/vite-plugin-inspect@0.7.37(@nuxt/kit@3.6.5)(rollup@3.26.2)(vite@4.4.7):
|
||||
resolution: {integrity: sha512-cRHzaE8g8/UUK0hA5DunAXiN3eJnq7Dpcu2bVf5dCRj/MYBKGeAv0Z27vYMhm2F/oeE5aG3+oYF4tkdhOlpXxg==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@nuxt/kit': '*'
|
||||
@@ -12008,7 +12005,7 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/node': 20.4.5
|
||||
esbuild: 0.18.17
|
||||
esbuild: 0.18.13
|
||||
postcss: 8.4.26
|
||||
rollup: 3.26.3
|
||||
optionalDependencies:
|
||||
|
||||
@@ -516,7 +516,6 @@ const select = {
|
||||
}
|
||||
|
||||
const selectMenu = {
|
||||
wrapper: 'relative',
|
||||
container: 'z-20',
|
||||
width: 'w-full',
|
||||
height: 'max-h-60',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div :class="ui.wrapper">
|
||||
<div :class="wrapperClass" v-bind="attrs">
|
||||
<table :class="[ui.base, ui.divide]">
|
||||
<thead :class="ui.thead">
|
||||
<tr :class="ui.tr.base">
|
||||
@@ -69,8 +69,10 @@
|
||||
<script lang="ts">
|
||||
import { ref, computed, defineComponent, toRaw } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { capitalize, orderBy, omit, get } from 'lodash-es'
|
||||
import { omit, capitalize, orderBy, get } from 'lodash-es'
|
||||
import { defu } from 'defu'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import type { Button } from '../../types/button'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
@@ -84,6 +86,7 @@ function defaultComparator<T> (a: T, z: T): boolean {
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Array,
|
||||
@@ -135,7 +138,7 @@ export default defineComponent({
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.table>>,
|
||||
default: () => appConfig.ui.table
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
@@ -143,7 +146,9 @@ export default defineComponent({
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.table>>(() => defu({}, props.ui, appConfig.ui.table))
|
||||
const ui = computed<Partial<typeof appConfig.ui.table>>(() => defuTwMerge({}, props.ui, appConfig.ui.table))
|
||||
|
||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
||||
|
||||
const columns = computed(() => props.columns ?? Object.keys(omit(props.rows[0] ?? {}, ['click'])).map((key) => ({ key, label: capitalize(key), sortable: false })))
|
||||
|
||||
@@ -235,8 +240,10 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui,
|
||||
wrapperClass,
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
sort,
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div :class="ui.wrapper">
|
||||
<div :class="wrapperClass">
|
||||
<HDisclosure v-for="(item, index) in items" v-slot="{ open, close }" :key="index" :default-open="defaultOpen || item.defaultOpen">
|
||||
<HDisclosureButton :ref="() => buttonRefs[index] = close" as="template" :disabled="item.disabled">
|
||||
<slot :item="item" :index="index" :open="open" :close="close">
|
||||
<UButton v-bind="{ ...omit(ui.default, ['openIcon', 'closeIcon']), ...$attrs, ...omit(item, ['slot', 'disabled', 'content', 'defaultOpen']) }">
|
||||
<UButton v-bind="{ ...omit(ui.default, ['openIcon', 'closeIcon']), ...attrs, ...omit(item, ['slot', 'disabled', 'content', 'defaultOpen']) }">
|
||||
<template #trailing>
|
||||
<UIcon
|
||||
:name="!open ? openIcon : closeIcon ? closeIcon : openIcon"
|
||||
@@ -43,10 +43,11 @@
|
||||
import { ref, computed, defineComponent } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { Disclosure as HDisclosure, DisclosureButton as HDisclosureButton, DisclosurePanel as HDisclosurePanel } from '@headlessui/vue'
|
||||
import { defu } from 'defu'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import UIcon from '../elements/Icon.vue'
|
||||
import UButton from '../elements/Button.vue'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import StateEmitter from '../../utils/StateEmitter'
|
||||
import type { AccordionItem } from '../../types/accordion'
|
||||
import { useAppConfig } from '#imports'
|
||||
@@ -87,17 +88,19 @@ export default defineComponent({
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.accordion>>,
|
||||
default: () => appConfig.ui.accordion
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
setup (props, { attrs }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.accordion>>(() => defu({}, props.ui, appConfig.ui.accordion))
|
||||
const ui = computed<Partial<typeof appConfig.ui.accordion>>(() => defuTwMerge({}, props.ui, appConfig.ui.accordion))
|
||||
|
||||
const uiButton = computed<Partial<typeof appConfig.ui.button>>(() => appConfig.ui.button)
|
||||
|
||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
||||
|
||||
const buttonRefs = ref<Function[]>([])
|
||||
|
||||
function closeOthers (itemIndex: number) {
|
||||
@@ -136,9 +139,11 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui,
|
||||
uiButton,
|
||||
wrapperClass,
|
||||
buttonRefs,
|
||||
closeOthers,
|
||||
omit,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div :class="alertClass">
|
||||
<div :class="alertClass" v-bind="attrs">
|
||||
<div class="flex gap-3" :class="{ 'items-start': (description || $slots.description), 'items-center': !description && !$slots.description }">
|
||||
<UIcon v-if="icon" :name="icon" :class="ui.icon.base" />
|
||||
<UAvatar v-if="avatar" v-bind="{ size: ui.avatar.size, ...avatar }" :class="ui.avatar.base" />
|
||||
@@ -34,17 +34,18 @@
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { twMerge, twJoin } from 'tailwind-merge'
|
||||
import UIcon from '../elements/Icon.vue'
|
||||
import UAvatar from '../elements/Avatar.vue'
|
||||
import UButton from '../elements/Button.vue'
|
||||
import type { Avatar } from '../../types/avatar'
|
||||
import type { Button } from '../../types/button'
|
||||
import { classNames } from '../../utils'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
// @ts-expect-error
|
||||
import appConfig from '#build/app.config'
|
||||
import { omit } from 'lodash-es'
|
||||
|
||||
// const appConfig = useAppConfig()
|
||||
|
||||
@@ -54,6 +55,7 @@ export default defineComponent({
|
||||
UAvatar,
|
||||
UButton
|
||||
},
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
@@ -98,29 +100,30 @@ export default defineComponent({
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.alert>>,
|
||||
default: () => appConfig.ui.alert
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
emits: ['close'],
|
||||
setup (props) {
|
||||
setup (props, { attrs }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.alert>>(() => defu({}, props.ui, appConfig.ui.alert))
|
||||
const ui = computed<Partial<typeof appConfig.ui.alert>>(() => defuTwMerge({}, props.ui, appConfig.ui.alert))
|
||||
|
||||
const alertClass = computed(() => {
|
||||
const variant = ui.value.color?.[props.color as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
||||
|
||||
return classNames(
|
||||
return twMerge(twJoin(
|
||||
ui.value.wrapper,
|
||||
ui.value.rounded,
|
||||
ui.value.shadow,
|
||||
ui.value.padding,
|
||||
variant?.replaceAll('{color}', props.color)
|
||||
)
|
||||
), attrs.class as string)
|
||||
})
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui,
|
||||
alertClass
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
<span :class="wrapperClass">
|
||||
<img
|
||||
v-if="url && !error"
|
||||
:class="avatarClass"
|
||||
:class="imgClass"
|
||||
:alt="alt"
|
||||
:src="url"
|
||||
v-bind="$attrs"
|
||||
v-bind="attrs"
|
||||
@error="onError"
|
||||
>
|
||||
<span v-else-if="text" :class="ui.text">{{ text }}</span>
|
||||
@@ -22,13 +22,14 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, computed, watch } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { twMerge, twJoin } from 'tailwind-merge'
|
||||
import UIcon from '../elements/Icon.vue'
|
||||
import { classNames } from '../../utils'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
// @ts-expect-error
|
||||
import appConfig from '#build/app.config'
|
||||
import { omit } from 'lodash-es'
|
||||
|
||||
// const appConfig = useAppConfig()
|
||||
|
||||
@@ -79,16 +80,20 @@ export default defineComponent({
|
||||
type: [String, Number],
|
||||
default: null
|
||||
},
|
||||
imgClass: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.avatar>>,
|
||||
default: () => appConfig.ui.avatar
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
setup (props, { attrs }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.avatar>>(() => defu({}, props.ui, appConfig.ui.avatar))
|
||||
const ui = computed<Partial<typeof appConfig.ui.avatar>>(() => defuTwMerge({}, props.ui, appConfig.ui.avatar))
|
||||
|
||||
const url = computed(() => {
|
||||
if (typeof props.src === 'boolean') {
|
||||
@@ -102,30 +107,30 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
const wrapperClass = computed(() => {
|
||||
return classNames(
|
||||
return twMerge(twJoin(
|
||||
ui.value.wrapper,
|
||||
(error.value || !url.value) && ui.value.background,
|
||||
ui.value.rounded,
|
||||
ui.value.size[props.size]
|
||||
)
|
||||
), attrs.class as string)
|
||||
})
|
||||
|
||||
const avatarClass = computed(() => {
|
||||
return classNames(
|
||||
const imgClass = computed(() => {
|
||||
return twMerge(twJoin(
|
||||
ui.value.rounded,
|
||||
ui.value.size[props.size]
|
||||
)
|
||||
), props.imgClass)
|
||||
})
|
||||
|
||||
const iconClass = computed(() => {
|
||||
return classNames(
|
||||
return twJoin(
|
||||
ui.value.icon.base,
|
||||
ui.value.icon.size[props.size]
|
||||
)
|
||||
})
|
||||
|
||||
const chipClass = computed(() => {
|
||||
return classNames(
|
||||
return twJoin(
|
||||
ui.value.chip.base,
|
||||
ui.value.chip.size[props.size],
|
||||
ui.value.chip.position[props.chipPosition],
|
||||
@@ -146,8 +151,10 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
wrapperClass,
|
||||
avatarClass,
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
imgClass,
|
||||
iconClass,
|
||||
chipClass,
|
||||
url,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { h, cloneVNode, computed, defineComponent } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { classNames, getSlotsChildren } from '../../utils'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge, twJoin } from 'tailwind-merge'
|
||||
import { defuTwMerge, getSlotsChildren } from '../../utils'
|
||||
import Avatar from './Avatar.vue'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
@@ -11,6 +12,7 @@ import appConfig from '#build/app.config'
|
||||
// const appConfig = useAppConfig()
|
||||
|
||||
export default defineComponent({
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
size: {
|
||||
type: String,
|
||||
@@ -25,14 +27,14 @@ export default defineComponent({
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.avatarGroup>>,
|
||||
default: () => appConfig.ui.avatarGroup
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
setup (props, { slots }) {
|
||||
setup (props, { attrs, slots }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.avatarGroup>>(() => defu({}, props.ui, appConfig.ui.avatarGroup))
|
||||
const ui = computed<Partial<typeof appConfig.ui.avatarGroup>>(() => defuTwMerge({}, props.ui, appConfig.ui.avatarGroup))
|
||||
|
||||
const children = computed(() => getSlotsChildren(slots))
|
||||
|
||||
@@ -46,13 +48,8 @@ export default defineComponent({
|
||||
vProps.size = props.size
|
||||
}
|
||||
|
||||
vProps.ui = node.props.ui || {}
|
||||
vProps.ui.wrapper = classNames(
|
||||
appConfig.ui.avatar.wrapper,
|
||||
vProps.ui.wrapper || '',
|
||||
ui.value.ring,
|
||||
ui.value.margin
|
||||
)
|
||||
vProps.class = node.props.class || ''
|
||||
vProps.class = twMerge(twJoin(vProps.class, ui.value.ring, ui.value.margin), vProps.class)
|
||||
|
||||
return cloneVNode(node, vProps)
|
||||
}
|
||||
@@ -61,19 +58,13 @@ export default defineComponent({
|
||||
return h(Avatar, {
|
||||
size: props.size,
|
||||
text: `+${children.value.length - max.value}`,
|
||||
ui: {
|
||||
wrapper: classNames(
|
||||
appConfig.ui.avatar.wrapper,
|
||||
ui.value.ring,
|
||||
ui.value.margin
|
||||
)
|
||||
}
|
||||
class: twJoin(ui.value.ring, ui.value.margin)
|
||||
})
|
||||
}
|
||||
|
||||
return null
|
||||
}).filter(Boolean).reverse())
|
||||
|
||||
return () => h('div', { class: ui.value.wrapper }, clones.value)
|
||||
return () => h('div', { class: twMerge(ui.value.wrapper, attrs.class as string), ...omit(attrs, ['class']) }, clones.value)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<span :class="badgeClass">
|
||||
<span :class="badgeClass" v-bind="attrs">
|
||||
<slot>{{ label }}</slot>
|
||||
</span>
|
||||
</template>
|
||||
@@ -7,8 +7,9 @@
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { classNames } from '../../utils'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge, twJoin } from 'tailwind-merge'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
// @ts-expect-error
|
||||
@@ -17,6 +18,7 @@ import appConfig from '#build/app.config'
|
||||
// const appConfig = useAppConfig()
|
||||
|
||||
export default defineComponent({
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
size: {
|
||||
type: String,
|
||||
@@ -48,28 +50,29 @@ export default defineComponent({
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.badge>>,
|
||||
default: () => appConfig.ui.badge
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
setup (props, { attrs }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.badge>>(() => defu({}, props.ui, appConfig.ui.badge))
|
||||
const ui = computed<Partial<typeof appConfig.ui.badge>>(() => defuTwMerge({}, props.ui, appConfig.ui.badge))
|
||||
|
||||
const badgeClass = computed(() => {
|
||||
const variant = ui.value.color?.[props.color as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
||||
|
||||
return classNames(
|
||||
return twMerge(twJoin(
|
||||
ui.value.base,
|
||||
ui.value.font,
|
||||
ui.value.rounded,
|
||||
ui.value.size[props.size],
|
||||
variant?.replaceAll('{color}', props.color)
|
||||
)
|
||||
), attrs.class as string)
|
||||
})
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
badgeClass
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<ULink :type="type" :disabled="disabled || loading" :class="buttonClass">
|
||||
<ULink :type="type" :disabled="disabled || loading" :class="buttonClass" v-bind="attrs">
|
||||
<slot name="leading" :disabled="disabled" :loading="loading">
|
||||
<UIcon v-if="isLeading && leadingIconName" :name="leadingIconName" :class="leadingIconClass" aria-hidden="true" />
|
||||
</slot>
|
||||
@@ -17,12 +17,13 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, useSlots } from 'vue'
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge, twJoin } from 'tailwind-merge'
|
||||
import UIcon from '../elements/Icon.vue'
|
||||
import ULink from '../elements/Link.vue'
|
||||
import { classNames } from '../../utils'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
// @ts-expect-error
|
||||
@@ -35,6 +36,7 @@ export default defineComponent({
|
||||
UIcon,
|
||||
ULink
|
||||
},
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
@@ -118,16 +120,14 @@ export default defineComponent({
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.button>>,
|
||||
default: () => appConfig.ui.button
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
setup (props, { attrs, slots }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const slots = useSlots()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.button>>(() => defu({}, props.ui, appConfig.ui.button))
|
||||
const ui = computed<Partial<typeof appConfig.ui.button>>(() => defuTwMerge({}, props.ui, appConfig.ui.button))
|
||||
|
||||
const isLeading = computed(() => {
|
||||
return (props.icon && props.leading) || (props.icon && !props.trailing) || (props.loading && !props.trailing) || props.leadingIcon
|
||||
@@ -142,7 +142,7 @@ export default defineComponent({
|
||||
const buttonClass = computed(() => {
|
||||
const variant = ui.value.color?.[props.color as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
||||
|
||||
return classNames(
|
||||
return twMerge(twJoin(
|
||||
ui.value.base,
|
||||
ui.value.font,
|
||||
ui.value.rounded,
|
||||
@@ -151,7 +151,7 @@ export default defineComponent({
|
||||
props.padded && ui.value[isSquare.value ? 'square' : 'padding'][props.size],
|
||||
variant?.replaceAll('{color}', props.color),
|
||||
props.block ? 'w-full flex justify-center items-center' : 'inline-flex items-center'
|
||||
)
|
||||
), attrs.class as string)
|
||||
})
|
||||
|
||||
const leadingIconName = computed(() => {
|
||||
@@ -171,7 +171,7 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
const leadingIconClass = computed(() => {
|
||||
return classNames(
|
||||
return twJoin(
|
||||
ui.value.icon.base,
|
||||
ui.value.icon.size[props.size],
|
||||
props.loading && 'animate-spin'
|
||||
@@ -179,7 +179,7 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
const trailingIconClass = computed(() => {
|
||||
return classNames(
|
||||
return twJoin(
|
||||
ui.value.icon.base,
|
||||
ui.value.icon.size[props.size],
|
||||
props.loading && !isLeading.value && 'animate-spin'
|
||||
@@ -187,6 +187,7 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
isLeading,
|
||||
isTrailing,
|
||||
isSquare,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { h, cloneVNode, computed, defineComponent } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { getSlotsChildren } from '../../utils'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge, twJoin } from 'tailwind-merge'
|
||||
import { defuTwMerge, getSlotsChildren } from '../../utils'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
// @ts-expect-error
|
||||
@@ -10,6 +11,7 @@ import appConfig from '#build/app.config'
|
||||
// const appConfig = useAppConfig()
|
||||
|
||||
export default defineComponent({
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
size: {
|
||||
type: String,
|
||||
@@ -20,14 +22,14 @@ export default defineComponent({
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.buttonGroup>>,
|
||||
default: () => appConfig.ui.buttonGroup
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
setup (props, { slots }) {
|
||||
setup (props, { attrs, slots }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.buttonGroup>>(() => defu({}, props.ui, appConfig.ui.buttonGroup))
|
||||
const ui = computed<Partial<typeof appConfig.ui.buttonGroup>>(() => defuTwMerge({}, props.ui, appConfig.ui.buttonGroup))
|
||||
|
||||
const children = computed(() => getSlotsChildren(slots))
|
||||
|
||||
@@ -50,22 +52,20 @@ export default defineComponent({
|
||||
vProps.size = props.size
|
||||
}
|
||||
|
||||
vProps.class = node.props?.class || ''
|
||||
vProps.class += ' !shadow-none'
|
||||
vProps.ui = node.props?.ui || {}
|
||||
vProps.ui.rounded = ''
|
||||
|
||||
const classes = ['shadow-none', 'rounded-none']
|
||||
if (index === 0) {
|
||||
vProps.ui.rounded = rounded.value.left
|
||||
classes.push(rounded.value.left)
|
||||
}
|
||||
if (index === children.value.length - 1) {
|
||||
classes.push(rounded.value.right)
|
||||
}
|
||||
|
||||
if (index === children.value.length - 1) {
|
||||
vProps.ui.rounded = rounded.value.right
|
||||
}
|
||||
vProps.class = node.props?.class || ''
|
||||
vProps.class = twMerge(twJoin(...classes), vProps.class)
|
||||
|
||||
return cloneVNode(node, vProps)
|
||||
}))
|
||||
|
||||
return () => h('div', { class: [ui.value.wrapper, ui.value.rounded, ui.value.shadow] }, clones.value)
|
||||
return () => h('div', { class: twMerge(twJoin(ui.value.wrapper, ui.value.rounded, ui.value.shadow), attrs.class as string), ...omit(attrs, ['class']) }, clones.value)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<HMenu v-slot="{ open }" as="div" :class="ui.wrapper" @mouseleave="onMouseLeave">
|
||||
<HMenu v-slot="{ open }" as="div" :class="wrapperClass" v-bind="attrs" @mouseleave="onMouseLeave">
|
||||
<HMenuButton
|
||||
ref="trigger"
|
||||
as="div"
|
||||
@@ -50,11 +50,13 @@ import type { PropType } from 'vue'
|
||||
import { Menu as HMenu, MenuButton as HMenuButton, MenuItems as HMenuItems, MenuItem as HMenuItem } from '@headlessui/vue'
|
||||
import { defu } from 'defu'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import UIcon from '../elements/Icon.vue'
|
||||
import UAvatar from '../elements/Avatar.vue'
|
||||
import UKbd from '../elements/Kbd.vue'
|
||||
import ULink from '../elements/Link.vue'
|
||||
import { usePopper } from '../../composables/usePopper'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import type { DropdownItem } from '../../types/dropdown'
|
||||
import type { PopperOptions } from '../../types'
|
||||
import { useAppConfig } from '#imports'
|
||||
@@ -75,6 +77,7 @@ export default defineComponent({
|
||||
UKbd,
|
||||
ULink
|
||||
},
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
items: {
|
||||
type: Array as PropType<DropdownItem[][]>,
|
||||
@@ -105,14 +108,14 @@ export default defineComponent({
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.dropdown>>,
|
||||
default: () => appConfig.ui.dropdown
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
setup (props, { attrs }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.dropdown>>(() => defu({}, props.ui, appConfig.ui.dropdown))
|
||||
const ui = computed<Partial<typeof appConfig.ui.dropdown>>(() => defuTwMerge({}, props.ui, appConfig.ui.dropdown))
|
||||
|
||||
const popper = computed<PopperOptions>(() => defu(props.mode === 'hover' ? { offsetDistance: 0 } : {}, props.popper, ui.value.popper as PopperOptions))
|
||||
|
||||
@@ -142,6 +145,8 @@ export default defineComponent({
|
||||
return props.mode === 'hover' ? { paddingTop: `${offsetDistance}px`, paddingBottom: `${offsetDistance}px` } : {}
|
||||
})
|
||||
|
||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
||||
|
||||
function onMouseOver () {
|
||||
if (props.mode !== 'hover' || !menuApi.value) {
|
||||
return
|
||||
@@ -183,11 +188,13 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui,
|
||||
trigger,
|
||||
container,
|
||||
containerStyle,
|
||||
wrapperClass,
|
||||
onMouseOver,
|
||||
onMouseLeave,
|
||||
omit
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<kbd :class="[ui.base, ui.size[size], ui.padding, ui.rounded, ui.font, ui.background, ui.ring]">
|
||||
<kbd :class="kbdClass" v-bind="attrs">
|
||||
<slot>{{ value }}</slot>
|
||||
</kbd>
|
||||
</template>
|
||||
@@ -7,7 +7,9 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge, twJoin } from 'tailwind-merge'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
// @ts-expect-error
|
||||
@@ -16,6 +18,7 @@ import appConfig from '#build/app.config'
|
||||
// const appConfig = useAppConfig()
|
||||
|
||||
export default defineComponent({
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
@@ -30,18 +33,32 @@ export default defineComponent({
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.kbd>>,
|
||||
default: () => appConfig.ui.kbd
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
setup (props, { attrs }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.kbd>>(() => defu({}, props.ui, appConfig.ui.kbd))
|
||||
const ui = computed<Partial<typeof appConfig.ui.kbd>>(() => defuTwMerge({}, props.ui, appConfig.ui.kbd))
|
||||
|
||||
const kbdClass = computed(() => {
|
||||
return twMerge(twJoin(
|
||||
ui.value.base,
|
||||
ui.value.size[props.size],
|
||||
ui.value.padding,
|
||||
ui.value.rounded,
|
||||
ui.value.font,
|
||||
ui.value.background,
|
||||
ui.value.ring
|
||||
), attrs.class as string)
|
||||
})
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui
|
||||
ui,
|
||||
kbdClass
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div :class="ui.wrapper">
|
||||
<div :class="wrapperClass">
|
||||
<div class="flex items-center h-5">
|
||||
<input
|
||||
:id="name"
|
||||
@@ -13,7 +13,7 @@
|
||||
type="checkbox"
|
||||
class="form-checkbox"
|
||||
:class="inputClass"
|
||||
v-bind="$attrs"
|
||||
v-bind="attrs"
|
||||
@change="onChange"
|
||||
>
|
||||
</div>
|
||||
@@ -32,8 +32,9 @@
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { classNames } from '../../utils'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge, twJoin } from 'tailwind-merge'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import { useFormGroup } from '../../composables/useFormGroup'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
@@ -88,17 +89,21 @@ export default defineComponent({
|
||||
return appConfig.ui.colors.includes(value)
|
||||
}
|
||||
},
|
||||
inputClass: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.checkbox>>,
|
||||
default: () => appConfig.ui.checkbox
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
emits: ['update:modelValue', 'change'],
|
||||
setup (props, { emit }) {
|
||||
setup (props, { emit, attrs }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.checkbox>>(() => defu({}, props.ui, appConfig.ui.checkbox))
|
||||
const ui = computed<Partial<typeof appConfig.ui.checkbox>>(() => defuTwMerge({}, props.ui, appConfig.ui.checkbox))
|
||||
|
||||
const { emitFormChange, formGroup } = useFormGroup()
|
||||
const color = computed(() => formGroup?.error?.value ? 'red' : props.color)
|
||||
@@ -117,21 +122,26 @@ export default defineComponent({
|
||||
emitFormChange()
|
||||
}
|
||||
|
||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
||||
|
||||
const inputClass = computed(() => {
|
||||
return classNames(
|
||||
return twMerge(twJoin(
|
||||
ui.value.base,
|
||||
ui.value.rounded,
|
||||
ui.value.background,
|
||||
ui.value.border,
|
||||
ui.value.ring.replaceAll('{color}', color.value),
|
||||
ui.value.color.replaceAll('{color}', color.value)
|
||||
)
|
||||
), props.inputClass)
|
||||
})
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui,
|
||||
toggle,
|
||||
wrapperClass,
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
inputClass,
|
||||
onChange
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div :class="ui.wrapper">
|
||||
<div :class="wrapperClass" v-bind="attrs">
|
||||
<label>
|
||||
<div v-if="label" :class="[ui.label.wrapper, size]">
|
||||
<p :class="[ui.label.base, required ? ui.label.required : '']">{{ label }}</p>
|
||||
@@ -11,7 +11,7 @@
|
||||
<div :class="[label ? ui.container : '']">
|
||||
<slot v-bind="{ error }" />
|
||||
|
||||
<p v-if="error" :class="[ui.error, size]">{{ error }}</p>
|
||||
<p v-if="error && typeof error !== 'boolean'" :class="[ui.error, size]">{{ error }}</p>
|
||||
<p v-else-if="help" :class="[ui.help, size]">{{ help }}</p>
|
||||
</div>
|
||||
</label>
|
||||
@@ -21,9 +21,11 @@
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, provide, inject } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { FormError } from '../../types'
|
||||
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
// @ts-expect-error
|
||||
import appConfig from '#build/app.config'
|
||||
@@ -31,6 +33,7 @@ import appConfig from '#build/app.config'
|
||||
// const appConfig = useAppConfig()
|
||||
|
||||
export default defineComponent({
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
@@ -69,14 +72,16 @@ export default defineComponent({
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.formGroup>>,
|
||||
default: () => appConfig.ui.formGroup
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
setup (props, { attrs }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.formGroup>>(() => defu({}, props.ui, appConfig.ui.formGroup))
|
||||
const ui = computed<Partial<typeof appConfig.ui.formGroup>>(() => defuTwMerge({}, props.ui, appConfig.ui.formGroup))
|
||||
|
||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
||||
|
||||
const formErrors = inject<Ref<FormError[]> | null>('form-errors', null)
|
||||
|
||||
@@ -95,8 +100,10 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui,
|
||||
wrapperClass,
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
size,
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div :class="ui.wrapper">
|
||||
<div :class="wrapperClass">
|
||||
<input
|
||||
ref="input"
|
||||
:name="name"
|
||||
@@ -10,7 +10,7 @@
|
||||
:disabled="disabled || loading"
|
||||
class="form-input"
|
||||
:class="inputClass"
|
||||
v-bind="$attrs"
|
||||
v-bind="attrs"
|
||||
@input="onInput"
|
||||
@blur="onBlur"
|
||||
>
|
||||
@@ -33,10 +33,11 @@
|
||||
<script lang="ts">
|
||||
import { ref, computed, onMounted, defineComponent } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge, twJoin } from 'tailwind-merge'
|
||||
import UIcon from '../elements/Icon.vue'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import { useFormGroup } from '../../composables/useFormGroup'
|
||||
import { classNames } from '../../utils'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
// @ts-expect-error
|
||||
@@ -134,17 +135,21 @@ export default defineComponent({
|
||||
].includes(value)
|
||||
}
|
||||
},
|
||||
inputClass: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.input>>,
|
||||
default: () => appConfig.ui.input
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
emits: ['update:modelValue', 'blur'],
|
||||
setup (props, { emit, slots }) {
|
||||
setup (props, { emit, attrs, slots }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.input>>(() => defu({}, props.ui, appConfig.ui.input))
|
||||
const ui = computed<Partial<typeof appConfig.ui.input>>(() => defuTwMerge({}, props.ui, appConfig.ui.input))
|
||||
|
||||
const { emitFormBlur, emitFormInput, formGroup } = useFormGroup()
|
||||
const color = computed(() => formGroup?.error?.value ? 'red' : props.color)
|
||||
@@ -174,10 +179,12 @@ export default defineComponent({
|
||||
}, 100)
|
||||
})
|
||||
|
||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
||||
|
||||
const inputClass = computed(() => {
|
||||
const variant = ui.value.color?.[color.value as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
||||
|
||||
return classNames(
|
||||
return twMerge(twJoin(
|
||||
ui.value.base,
|
||||
ui.value.rounded,
|
||||
ui.value.placeholder,
|
||||
@@ -186,7 +193,7 @@ export default defineComponent({
|
||||
variant?.replaceAll('{color}', color.value),
|
||||
(isLeading.value || slots.leading) && ui.value.leading.padding[size.value],
|
||||
(isTrailing.value || slots.trailing) && ui.value.trailing.padding[size.value]
|
||||
)
|
||||
), props.inputClass)
|
||||
})
|
||||
|
||||
const isLeading = computed(() => {
|
||||
@@ -214,7 +221,7 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
const leadingWrapperIconClass = computed(() => {
|
||||
return classNames(
|
||||
return twJoin(
|
||||
ui.value.icon.leading.wrapper,
|
||||
ui.value.icon.leading.pointer,
|
||||
ui.value.icon.leading.padding[size.value]
|
||||
@@ -222,7 +229,7 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
const leadingIconClass = computed(() => {
|
||||
return classNames(
|
||||
return twJoin(
|
||||
ui.value.icon.base,
|
||||
appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll('{color}', color.value),
|
||||
ui.value.icon.size[size.value],
|
||||
@@ -231,7 +238,7 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
const trailingWrapperIconClass = computed(() => {
|
||||
return classNames(
|
||||
return twJoin(
|
||||
ui.value.icon.trailing.wrapper,
|
||||
ui.value.icon.trailing.pointer,
|
||||
ui.value.icon.trailing.padding[size.value]
|
||||
@@ -239,7 +246,7 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
const trailingIconClass = computed(() => {
|
||||
return classNames(
|
||||
return twJoin(
|
||||
ui.value.icon.base,
|
||||
appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll('{color}', color.value),
|
||||
ui.value.icon.size[size.value],
|
||||
@@ -248,11 +255,14 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui,
|
||||
input,
|
||||
isLeading,
|
||||
isTrailing,
|
||||
wrapperClass,
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
inputClass,
|
||||
leadingIconName,
|
||||
leadingIconClass,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div :class="ui.wrapper">
|
||||
<div :class="wrapperClass">
|
||||
<div class="flex items-center h-5">
|
||||
<input
|
||||
v-model="pick"
|
||||
@@ -10,7 +10,7 @@
|
||||
type="radio"
|
||||
class="form-radio"
|
||||
:class="inputClass"
|
||||
v-bind="$attrs"
|
||||
v-bind="attrs"
|
||||
>
|
||||
</div>
|
||||
<div v-if="label || $slots.label" class="ms-3 text-sm">
|
||||
@@ -28,8 +28,9 @@
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { classNames } from '../../utils'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge, twJoin } from 'tailwind-merge'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import { useFormGroup } from '../../composables/useFormGroup'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
@@ -76,17 +77,21 @@ export default defineComponent({
|
||||
return appConfig.ui.colors.includes(value)
|
||||
}
|
||||
},
|
||||
inputClass: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.radio>>,
|
||||
default: () => appConfig.ui.radio
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
setup (props, { emit }) {
|
||||
setup (props, { emit, attrs }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.radio>>(() => defu({}, props.ui, appConfig.ui.radio))
|
||||
const ui = computed<Partial<typeof appConfig.ui.radio>>(() => defuTwMerge({}, props.ui, appConfig.ui.radio))
|
||||
|
||||
const { emitFormChange, formGroup } = useFormGroup()
|
||||
const color = computed(() => formGroup?.error?.value ? 'red' : props.color)
|
||||
@@ -103,20 +108,25 @@ export default defineComponent({
|
||||
}
|
||||
})
|
||||
|
||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
||||
|
||||
const inputClass = computed(() => {
|
||||
return classNames(
|
||||
return twMerge(twJoin(
|
||||
ui.value.base,
|
||||
ui.value.background,
|
||||
ui.value.border,
|
||||
ui.value.ring.replaceAll('{color}', color.value),
|
||||
ui.value.color.replaceAll('{color}', color.value)
|
||||
)
|
||||
), props.inputClass)
|
||||
})
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui,
|
||||
pick,
|
||||
wrapperClass,
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
inputClass
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
:step="step"
|
||||
type="range"
|
||||
:class="[inputClass, thumbClass, trackClass]"
|
||||
v-bind="$attrs"
|
||||
v-bind="attrs"
|
||||
@change="onChange"
|
||||
>
|
||||
|
||||
@@ -21,8 +21,9 @@
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { classNames } from '../../utils'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge, twJoin } from 'tailwind-merge'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import { useFormGroup } from '../../composables/useFormGroup'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
@@ -70,17 +71,21 @@ export default defineComponent({
|
||||
return appConfig.ui.colors.includes(value)
|
||||
}
|
||||
},
|
||||
inputClass: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.range>>,
|
||||
default: () => appConfig.ui.range
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
emits: ['update:modelValue', 'change'],
|
||||
setup (props, { emit }) {
|
||||
setup (props, { emit, attrs }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.range>>(() => defu({}, props.ui, appConfig.ui.range))
|
||||
const ui = computed<Partial<typeof appConfig.ui.range>>(() => defuTwMerge({}, props.ui, appConfig.ui.range))
|
||||
|
||||
const { emitFormChange, formGroup } = useFormGroup()
|
||||
const color = computed(() => formGroup?.error?.value ? 'red' : props.color)
|
||||
@@ -101,24 +106,24 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
const wrapperClass = computed(() => {
|
||||
return classNames(
|
||||
return twMerge(twJoin(
|
||||
ui.value.wrapper,
|
||||
ui.value.size[size.value]
|
||||
)
|
||||
), attrs.class as string)
|
||||
})
|
||||
|
||||
const inputClass = computed(() => {
|
||||
return classNames(
|
||||
return twMerge(twJoin(
|
||||
ui.value.base,
|
||||
ui.value.background,
|
||||
ui.value.rounded,
|
||||
ui.value.ring.replaceAll('{color}', color.value),
|
||||
ui.value.size[size.value]
|
||||
)
|
||||
), props.inputClass)
|
||||
})
|
||||
|
||||
const thumbClass = computed(() => {
|
||||
return classNames(
|
||||
return twJoin(
|
||||
ui.value.thumb.base,
|
||||
// Intermediate class to allow thumb ring or background color (set to `current`) as it's impossible to safelist with arbitrary values
|
||||
ui.value.thumb.color.replaceAll('{color}', color.value),
|
||||
@@ -129,7 +134,7 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
const trackClass = computed(() => {
|
||||
return classNames(
|
||||
return twJoin(
|
||||
ui.value.track.base,
|
||||
ui.value.track.background,
|
||||
ui.value.track.rounded,
|
||||
@@ -138,7 +143,7 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
const progressClass = computed(() => {
|
||||
return classNames(
|
||||
return twJoin(
|
||||
ui.value.progress.base,
|
||||
ui.value.progress.rounded,
|
||||
ui.value.progress.background.replaceAll('{color}', color.value),
|
||||
@@ -156,10 +161,12 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui,
|
||||
value,
|
||||
wrapperClass,
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
inputClass,
|
||||
thumbClass,
|
||||
trackClass,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div :class="ui.wrapper">
|
||||
<div :class="wrapperClass">
|
||||
<select
|
||||
:name="name"
|
||||
:value="modelValue"
|
||||
@@ -7,7 +7,7 @@
|
||||
:disabled="disabled || loading"
|
||||
class="form-select"
|
||||
:class="selectClass"
|
||||
v-bind="$attrs"
|
||||
v-bind="attrs"
|
||||
@input="onInput"
|
||||
@change="onChange"
|
||||
>
|
||||
@@ -55,10 +55,10 @@
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import type { PropType, ComputedRef } from 'vue'
|
||||
import { get } from 'lodash-es'
|
||||
import { defu } from 'defu'
|
||||
import { get, omit } from 'lodash-es'
|
||||
import { twMerge, twJoin } from 'tailwind-merge'
|
||||
import UIcon from '../elements/Icon.vue'
|
||||
import { classNames } from '../../utils'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import { useFormGroup } from '../../composables/useFormGroup'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
@@ -161,17 +161,21 @@ export default defineComponent({
|
||||
type: String,
|
||||
default: 'value'
|
||||
},
|
||||
selectClass: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.select>>,
|
||||
default: () => appConfig.ui.select
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
emits: ['update:modelValue', 'change'],
|
||||
setup (props, { emit, slots }) {
|
||||
setup (props, { emit, attrs, slots }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.select>>(() => defu({}, props.ui, appConfig.ui.select))
|
||||
const ui = computed<Partial<typeof appConfig.ui.select>>(() => defuTwMerge({}, props.ui, appConfig.ui.select))
|
||||
|
||||
const { emitFormChange, formGroup } = useFormGroup()
|
||||
const color = computed(() => formGroup?.error?.value ? 'red' : props.color)
|
||||
@@ -239,10 +243,12 @@ export default defineComponent({
|
||||
return foundOption[props.valueAttribute]
|
||||
})
|
||||
|
||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
||||
|
||||
const selectClass = computed(() => {
|
||||
const variant = ui.value.color?.[color.value as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
||||
|
||||
return classNames(
|
||||
return twMerge(twJoin(
|
||||
ui.value.base,
|
||||
ui.value.rounded,
|
||||
ui.value.size[size.value],
|
||||
@@ -250,7 +256,7 @@ export default defineComponent({
|
||||
variant?.replaceAll('{color}', color.value),
|
||||
(isLeading.value || slots.leading) && ui.value.leading.padding[size.value],
|
||||
(isTrailing.value || slots.trailing) && ui.value.trailing.padding[size.value]
|
||||
)
|
||||
), props.selectClass)
|
||||
})
|
||||
|
||||
const isLeading = computed(() => {
|
||||
@@ -278,7 +284,7 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
const leadingWrapperIconClass = computed(() => {
|
||||
return classNames(
|
||||
return twJoin(
|
||||
ui.value.icon.leading.wrapper,
|
||||
ui.value.icon.leading.pointer,
|
||||
ui.value.icon.leading.padding[size.value]
|
||||
@@ -286,7 +292,7 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
const leadingIconClass = computed(() => {
|
||||
return classNames(
|
||||
return twJoin(
|
||||
ui.value.icon.base,
|
||||
appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll('{color}', color.value),
|
||||
ui.value.icon.size[size.value],
|
||||
@@ -295,7 +301,7 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
const trailingWrapperIconClass = computed(() => {
|
||||
return classNames(
|
||||
return twJoin(
|
||||
ui.value.icon.trailing.wrapper,
|
||||
ui.value.icon.trailing.pointer,
|
||||
ui.value.icon.trailing.padding[size.value]
|
||||
@@ -303,7 +309,7 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
const trailingIconClass = computed(() => {
|
||||
return classNames(
|
||||
return twJoin(
|
||||
ui.value.icon.base,
|
||||
appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll('{color}', color.value),
|
||||
ui.value.icon.size[size.value],
|
||||
@@ -312,12 +318,15 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui,
|
||||
normalizedOptionsWithPlaceholder,
|
||||
normalizedValue,
|
||||
isLeading,
|
||||
isTrailing,
|
||||
wrapperClass,
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
selectClass,
|
||||
leadingIconName,
|
||||
leadingIconClass,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
:multiple="multiple"
|
||||
:disabled="disabled || loading"
|
||||
as="div"
|
||||
:class="uiMenu.wrapper"
|
||||
:class="wrapperClass"
|
||||
@update:model-value="onUpdate"
|
||||
>
|
||||
<input
|
||||
@@ -28,7 +28,7 @@
|
||||
class="inline-flex w-full"
|
||||
>
|
||||
<slot :open="open" :disabled="disabled" :loading="loading">
|
||||
<button :class="selectClass" :disabled="disabled || loading" type="button" v-bind="$attrs">
|
||||
<button :class="selectClass" :disabled="disabled || loading" type="button" v-bind="attrs">
|
||||
<span v-if="(isLeading && leadingIconName) || $slots.leading" :class="leadingWrapperIconClass">
|
||||
<slot name="leading" :disabled="disabled" :loading="loading">
|
||||
<UIcon :name="leadingIconName" :class="leadingIconClass" />
|
||||
@@ -131,9 +131,11 @@ import {
|
||||
} from '@headlessui/vue'
|
||||
import { computedAsync, useDebounceFn } from '@vueuse/core'
|
||||
import { defu } from 'defu'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge, twJoin } from 'tailwind-merge'
|
||||
import UIcon from '../elements/Icon.vue'
|
||||
import UAvatar from '../elements/Avatar.vue'
|
||||
import { classNames } from '../../utils'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import { usePopper } from '../../composables/usePopper'
|
||||
import { useFormGroup } from '../../composables/useFormGroup'
|
||||
import type { PopperOptions } from '../../types'
|
||||
@@ -284,22 +286,26 @@ export default defineComponent({
|
||||
type: Object as PropType<PopperOptions>,
|
||||
default: () => ({})
|
||||
},
|
||||
selectClass: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.select>>,
|
||||
default: () => appConfig.ui.select
|
||||
default: () => ({})
|
||||
},
|
||||
uiMenu: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.selectMenu>>,
|
||||
default: () => appConfig.ui.selectMenu
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
emits: ['update:modelValue', 'open', 'close', 'change'],
|
||||
setup (props, { emit, slots }) {
|
||||
setup (props, { emit, attrs, slots }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.select>>(() => defu({}, props.ui, appConfig.ui.select))
|
||||
const uiMenu = computed<Partial<typeof appConfig.ui.selectMenu>>(() => defu({}, props.uiMenu, appConfig.ui.selectMenu))
|
||||
const ui = computed<Partial<typeof appConfig.ui.select>>(() => defuTwMerge({}, props.ui, appConfig.ui.select))
|
||||
const uiMenu = computed<Partial<typeof appConfig.ui.selectMenu>>(() => defuTwMerge({}, props.uiMenu, appConfig.ui.selectMenu))
|
||||
|
||||
const popper = computed<PopperOptions>(() => defu({}, props.popper, uiMenu.value.popper as PopperOptions))
|
||||
|
||||
@@ -311,10 +317,12 @@ export default defineComponent({
|
||||
const query = ref('')
|
||||
const searchInput = ref<ComponentPublicInstance<HTMLElement>>()
|
||||
|
||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
||||
|
||||
const selectClass = computed(() => {
|
||||
const variant = ui.value.color?.[color.value as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
||||
|
||||
return classNames(
|
||||
return twMerge(twJoin(
|
||||
ui.value.base,
|
||||
ui.value.rounded,
|
||||
'text-left cursor-default',
|
||||
@@ -325,7 +333,7 @@ export default defineComponent({
|
||||
(isLeading.value || slots.leading) && ui.value.leading.padding[size.value],
|
||||
(isTrailing.value || slots.trailing) && ui.value.trailing.padding[size.value],
|
||||
'inline-flex items-center'
|
||||
)
|
||||
), props.selectClass)
|
||||
})
|
||||
|
||||
const isLeading = computed(() => {
|
||||
@@ -353,7 +361,7 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
const leadingWrapperIconClass = computed(() => {
|
||||
return classNames(
|
||||
return twJoin(
|
||||
ui.value.icon.leading.wrapper,
|
||||
ui.value.icon.leading.pointer,
|
||||
ui.value.icon.leading.padding[size.value]
|
||||
@@ -361,7 +369,7 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
const leadingIconClass = computed(() => {
|
||||
return classNames(
|
||||
return twJoin(
|
||||
ui.value.icon.base,
|
||||
appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll('{color}', color.value),
|
||||
ui.value.icon.size[size.value],
|
||||
@@ -370,7 +378,7 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
const trailingWrapperIconClass = computed(() => {
|
||||
return classNames(
|
||||
return twJoin(
|
||||
ui.value.icon.trailing.wrapper,
|
||||
ui.value.icon.trailing.pointer,
|
||||
ui.value.icon.trailing.padding[size.value]
|
||||
@@ -378,7 +386,7 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
const trailingIconClass = computed(() => {
|
||||
return classNames(
|
||||
return twJoin(
|
||||
ui.value.icon.base,
|
||||
appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll('{color}', color.value),
|
||||
ui.value.icon.size[size.value],
|
||||
@@ -429,12 +437,15 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
uiMenu,
|
||||
trigger,
|
||||
container,
|
||||
isLeading,
|
||||
isTrailing,
|
||||
wrapperClass,
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
selectClass,
|
||||
leadingIconName,
|
||||
leadingIconClass,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div :class="ui.wrapper">
|
||||
<div :class="wrapperClass">
|
||||
<textarea
|
||||
ref="textarea"
|
||||
:value="modelValue"
|
||||
@@ -10,7 +10,7 @@
|
||||
:placeholder="placeholder"
|
||||
class="form-textarea"
|
||||
:class="textareaClass"
|
||||
v-bind="$attrs"
|
||||
v-bind="attrs"
|
||||
@input="onInput"
|
||||
@blur="onBlur"
|
||||
/>
|
||||
@@ -20,8 +20,9 @@
|
||||
<script lang="ts">
|
||||
import { ref, computed, watch, onMounted, nextTick, defineComponent } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { classNames } from '../../utils'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge, twJoin } from 'tailwind-merge'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import { useFormGroup } from '../../composables/useFormGroup'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
@@ -97,19 +98,23 @@ export default defineComponent({
|
||||
].includes(value)
|
||||
}
|
||||
},
|
||||
textareaClass: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.textarea>>,
|
||||
default: () => appConfig.ui.textarea
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
emits: ['update:modelValue', 'blur'],
|
||||
setup (props, { emit }) {
|
||||
setup (props, { emit, attrs }) {
|
||||
const textarea = ref<HTMLTextAreaElement | null>(null)
|
||||
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.textarea>>(() => defu({}, props.ui, appConfig.ui.textarea))
|
||||
const ui = computed<Partial<typeof appConfig.ui.textarea>>(() => defuTwMerge({}, props.ui, appConfig.ui.textarea))
|
||||
|
||||
const { emitFormBlur, emitFormInput, formGroup } = useFormGroup()
|
||||
const color = computed(() => formGroup?.error?.value ? 'red' : props.color)
|
||||
@@ -172,10 +177,12 @@ export default defineComponent({
|
||||
}, 100)
|
||||
})
|
||||
|
||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
||||
|
||||
const textareaClass = computed(() => {
|
||||
const variant = ui.value.color?.[color.value as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
||||
|
||||
return classNames(
|
||||
return twMerge(twJoin(
|
||||
ui.value.base,
|
||||
ui.value.rounded,
|
||||
ui.value.placeholder,
|
||||
@@ -183,13 +190,16 @@ export default defineComponent({
|
||||
props.padded ? ui.value.padding[size.value] : 'p-0',
|
||||
variant?.replaceAll('{color}', color.value),
|
||||
!props.resize && 'resize-none'
|
||||
)
|
||||
), props.textareaClass)
|
||||
})
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui,
|
||||
textarea,
|
||||
wrapperClass,
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
textareaClass,
|
||||
onInput,
|
||||
onBlur
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
:name="name"
|
||||
:disabled="disabled"
|
||||
:class="switchClass"
|
||||
v-bind="attrs"
|
||||
>
|
||||
<span :class="[active ? ui.container.active : ui.container.inactive, ui.container.base]">
|
||||
<span v-if="onIcon" :class="[active ? ui.icon.active : ui.icon.inactive, ui.icon.base]" aria-hidden="true">
|
||||
@@ -19,10 +20,11 @@
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { Switch as HSwitch } from '@headlessui/vue'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge, twJoin } from 'tailwind-merge'
|
||||
import UIcon from '../elements/Icon.vue'
|
||||
import { classNames } from '../../utils'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import { useFormGroup } from '../../composables/useFormGroup'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
@@ -36,6 +38,7 @@ export default defineComponent({
|
||||
HSwitch,
|
||||
UIcon
|
||||
},
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
@@ -66,15 +69,15 @@ export default defineComponent({
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.toggle>>,
|
||||
default: () => appConfig.ui.toggle
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
setup (props, { emit }) {
|
||||
setup (props, { emit, attrs }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.toggle>>(() => defu({}, props.ui, appConfig.ui.toggle))
|
||||
const ui = computed<Partial<typeof appConfig.ui.toggle>>(() => defuTwMerge({}, props.ui, appConfig.ui.toggle))
|
||||
|
||||
const { emitFormChange, formGroup } = useFormGroup()
|
||||
const color = computed(() => formGroup?.error?.value ? 'red' : props.color)
|
||||
@@ -90,27 +93,28 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
const switchClass = computed(() => {
|
||||
return classNames(
|
||||
return twMerge(twJoin(
|
||||
ui.value.base,
|
||||
ui.value.rounded,
|
||||
ui.value.ring.replaceAll('{color}', color.value),
|
||||
(active.value ? ui.value.active : ui.value.inactive).replaceAll('{color}', color.value)
|
||||
)
|
||||
), attrs.class as string)
|
||||
})
|
||||
|
||||
const onIconClass = computed(() => {
|
||||
return classNames(
|
||||
return twJoin(
|
||||
ui.value.icon.on.replaceAll('{color}', color.value)
|
||||
)
|
||||
})
|
||||
|
||||
const offIconClass = computed(() => {
|
||||
return classNames(
|
||||
return twJoin(
|
||||
ui.value.icon.off.replaceAll('{color}', color.value)
|
||||
)
|
||||
})
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui,
|
||||
active,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<component
|
||||
:is="$attrs.onSubmit ? 'form' : as"
|
||||
:class="[ui.base, ui.rounded, ui.divide, ui.ring, ui.shadow, ui.background]"
|
||||
v-bind="$attrs"
|
||||
:class="cardClass"
|
||||
v-bind="attrs"
|
||||
>
|
||||
<div v-if="$slots.header" :class="[ui.header.base, ui.header.padding, ui.header.background]">
|
||||
<slot name="header" />
|
||||
@@ -19,7 +19,9 @@
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge, twJoin } from 'tailwind-merge'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
// @ts-expect-error
|
||||
@@ -36,18 +38,31 @@ export default defineComponent({
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.card>>,
|
||||
default: () => appConfig.ui.card
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
setup (props, { attrs }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.card>>(() => defu({}, props.ui, appConfig.ui.card))
|
||||
const ui = computed<Partial<typeof appConfig.ui.card>>(() => defuTwMerge({}, props.ui, appConfig.ui.card))
|
||||
|
||||
const cardClass = computed(() => {
|
||||
return twMerge(twJoin(
|
||||
ui.value.base,
|
||||
ui.value.rounded,
|
||||
ui.value.divide,
|
||||
ui.value.ring,
|
||||
ui.value.shadow,
|
||||
ui.value.background
|
||||
), attrs.class as string)
|
||||
})
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui
|
||||
ui,
|
||||
cardClass
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<component :is="as" :class="[ui.base, ui.padding, ui.constrained]">
|
||||
<component :is="as" :class="containerClass" v-bind="attrs">
|
||||
<slot />
|
||||
</component>
|
||||
</template>
|
||||
@@ -7,7 +7,9 @@
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge, twJoin } from 'tailwind-merge'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
// @ts-expect-error
|
||||
@@ -16,6 +18,7 @@ import appConfig from '#build/app.config'
|
||||
// const appConfig = useAppConfig()
|
||||
|
||||
export default defineComponent({
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
as: {
|
||||
type: String,
|
||||
@@ -23,18 +26,28 @@ export default defineComponent({
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.container>>,
|
||||
default: () => appConfig.ui.container
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
setup (props, { attrs }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.container>>(() => defu({}, props.ui, appConfig.ui.container))
|
||||
const ui = computed<Partial<typeof appConfig.ui.container>>(() => defuTwMerge({}, props.ui, appConfig.ui.container))
|
||||
|
||||
const containerClass = computed(() => {
|
||||
return twMerge(twJoin(
|
||||
ui.value.base,
|
||||
ui.value.padding,
|
||||
ui.value.constrained
|
||||
), attrs.class as string)
|
||||
})
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui
|
||||
ui,
|
||||
containerClass
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
<template>
|
||||
<div :class="[ui.base, ui.background, ui.rounded]" />
|
||||
<div :class="skeletonClass" v-bind="attrs" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge, twJoin } from 'tailwind-merge'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
// @ts-expect-error
|
||||
@@ -14,21 +16,32 @@ import appConfig from '#build/app.config'
|
||||
// const appConfig = useAppConfig()
|
||||
|
||||
export default defineComponent({
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.skeleton>>,
|
||||
default: () => appConfig.ui.skeleton
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
setup (props, { attrs }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.skeleton>>(() => defu({}, props.ui, appConfig.ui.skeleton))
|
||||
const ui = computed<Partial<typeof appConfig.ui.skeleton>>(() => defuTwMerge({}, props.ui, appConfig.ui.skeleton))
|
||||
|
||||
const skeletonClass = computed(() => {
|
||||
return twMerge(twJoin(
|
||||
ui.value.base,
|
||||
ui.value.background,
|
||||
ui.value.rounded
|
||||
), attrs.class as string)
|
||||
})
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui
|
||||
ui,
|
||||
skeletonClass
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -4,64 +4,65 @@
|
||||
:model-value="modelValue"
|
||||
:multiple="multiple"
|
||||
:nullable="nullable"
|
||||
:class="wrapperClass"
|
||||
v-bind="attrs"
|
||||
as="div"
|
||||
@update:model-value="onSelect"
|
||||
>
|
||||
<div :class="ui.wrapper">
|
||||
<div v-show="searchable" :class="ui.input.wrapper">
|
||||
<UIcon v-if="iconName" :name="iconName" :class="iconClass" aria-hidden="true" />
|
||||
<HComboboxInput
|
||||
ref="comboboxInput"
|
||||
:value="query"
|
||||
:class="[ui.input.base, ui.input.size, ui.input.height, ui.input.padding, icon && ui.input.icon.padding]"
|
||||
:placeholder="placeholder"
|
||||
autocomplete="off"
|
||||
@change="query = $event.target.value"
|
||||
/>
|
||||
<div v-show="searchable" :class="ui.input.wrapper">
|
||||
<UIcon v-if="iconName" :name="iconName" :class="iconClass" aria-hidden="true" />
|
||||
<HComboboxInput
|
||||
ref="comboboxInput"
|
||||
:value="query"
|
||||
:class="[ui.input.base, ui.input.size, ui.input.height, ui.input.padding, icon && ui.input.icon.padding]"
|
||||
:placeholder="placeholder"
|
||||
autocomplete="off"
|
||||
@change="query = $event.target.value"
|
||||
/>
|
||||
|
||||
<UButton
|
||||
v-if="closeButton"
|
||||
v-bind="{ ...ui.default.closeButton, ...closeButton }"
|
||||
:class="ui.input.closeButton"
|
||||
aria-label="Close"
|
||||
@click="onClear"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<HComboboxOptions
|
||||
v-if="groups.length"
|
||||
static
|
||||
hold
|
||||
as="div"
|
||||
aria-label="Commands"
|
||||
:class="ui.container"
|
||||
>
|
||||
<CommandPaletteGroup
|
||||
v-for="group of groups"
|
||||
:key="group.key"
|
||||
:query="query"
|
||||
:group="group"
|
||||
:group-attribute="groupAttribute"
|
||||
:command-attribute="commandAttribute"
|
||||
:selected-icon="selectedIcon"
|
||||
:ui="ui"
|
||||
>
|
||||
<template v-for="(_, name) in $slots" #[name]="slotData">
|
||||
<slot :name="name" v-bind="slotData" />
|
||||
</template>
|
||||
</CommandPaletteGroup>
|
||||
</HComboboxOptions>
|
||||
|
||||
<template v-else-if="emptyState">
|
||||
<slot name="empty-state">
|
||||
<div :class="ui.emptyState.wrapper">
|
||||
<UIcon v-if="emptyState.icon" :name="emptyState.icon" :class="ui.emptyState.icon" aria-hidden="true" />
|
||||
<p :class="query ? ui.emptyState.queryLabel : ui.emptyState.label">
|
||||
{{ query ? emptyState.queryLabel : emptyState.label }}
|
||||
</p>
|
||||
</div>
|
||||
</slot>
|
||||
</template>
|
||||
<UButton
|
||||
v-if="closeButton"
|
||||
v-bind="{ ...ui.default.closeButton, ...closeButton }"
|
||||
:class="ui.input.closeButton"
|
||||
aria-label="Close"
|
||||
@click="onClear"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<HComboboxOptions
|
||||
v-if="groups.length"
|
||||
static
|
||||
hold
|
||||
as="div"
|
||||
aria-label="Commands"
|
||||
:class="ui.container"
|
||||
>
|
||||
<CommandPaletteGroup
|
||||
v-for="group of groups"
|
||||
:key="group.key"
|
||||
:query="query"
|
||||
:group="group"
|
||||
:group-attribute="groupAttribute"
|
||||
:command-attribute="commandAttribute"
|
||||
:selected-icon="selectedIcon"
|
||||
:ui="ui"
|
||||
>
|
||||
<template v-for="(_, name) in $slots" #[name]="slotData">
|
||||
<slot :name="name" v-bind="slotData" />
|
||||
</template>
|
||||
</CommandPaletteGroup>
|
||||
</HComboboxOptions>
|
||||
|
||||
<template v-else-if="emptyState">
|
||||
<slot name="empty-state">
|
||||
<div :class="ui.emptyState.wrapper">
|
||||
<UIcon v-if="emptyState.icon" :name="emptyState.icon" :class="ui.emptyState.icon" aria-hidden="true" />
|
||||
<p :class="query ? ui.emptyState.queryLabel : ui.emptyState.label">
|
||||
{{ query ? emptyState.queryLabel : emptyState.label }}
|
||||
</p>
|
||||
</div>
|
||||
</slot>
|
||||
</template>
|
||||
</HCombobox>
|
||||
</template>
|
||||
|
||||
@@ -71,7 +72,8 @@ import { Combobox as HCombobox, ComboboxInput as HComboboxInput, ComboboxOptions
|
||||
import type { ComputedRef, PropType, ComponentPublicInstance } from 'vue'
|
||||
import { useDebounceFn } from '@vueuse/core'
|
||||
import { useFuse } from '@vueuse/integrations/useFuse'
|
||||
import { groupBy, map } from 'lodash-es'
|
||||
import { twMerge, twJoin } from 'tailwind-merge'
|
||||
import { groupBy, map, omit } from 'lodash-es'
|
||||
import { defu } from 'defu'
|
||||
import type { UseFuseOptions } from '@vueuse/integrations/useFuse'
|
||||
import type { Group, Command } from '../../types/command-palette'
|
||||
@@ -79,7 +81,7 @@ import UIcon from '../elements/Icon.vue'
|
||||
import UButton from '../elements/Button.vue'
|
||||
import type { Button } from '../../types/button'
|
||||
import CommandPaletteGroup from './CommandPaletteGroup.vue'
|
||||
import { classNames } from '../../utils'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
// @ts-expect-error
|
||||
@@ -96,6 +98,7 @@ export default defineComponent({
|
||||
UButton,
|
||||
CommandPaletteGroup
|
||||
},
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
modelValue: {
|
||||
type: [String, Number, Object, Array],
|
||||
@@ -175,15 +178,15 @@ export default defineComponent({
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.commandPalette>>,
|
||||
default: () => appConfig.ui.commandPalette
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
emits: ['update:modelValue', 'close'],
|
||||
setup (props, { emit, expose }) {
|
||||
setup (props, { emit, attrs, expose }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.commandPalette>>(() => defu({}, props.ui, appConfig.ui.commandPalette))
|
||||
const ui = computed<Partial<typeof appConfig.ui.commandPalette>>(() => defuTwMerge({}, props.ui, appConfig.ui.commandPalette))
|
||||
|
||||
const query = ref('')
|
||||
const comboboxInput = ref<ComponentPublicInstance<HTMLInputElement>>()
|
||||
@@ -268,6 +271,8 @@ export default defineComponent({
|
||||
}, 0)
|
||||
})
|
||||
|
||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
||||
|
||||
const iconName = computed(() => {
|
||||
if ((props.loading || isLoading.value) && props.loadingIcon) {
|
||||
return props.loadingIcon
|
||||
@@ -277,7 +282,7 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
const iconClass = computed(() => {
|
||||
return classNames(
|
||||
return twJoin(
|
||||
ui.value.input.icon.base,
|
||||
ui.value.input.icon.size,
|
||||
((props.loading || isLoading.value) && props.loadingIcon) && 'animate-spin'
|
||||
@@ -325,12 +330,14 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui,
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
groups,
|
||||
comboboxInput,
|
||||
query,
|
||||
wrapperClass,
|
||||
iconName,
|
||||
iconClass,
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
|
||||
@@ -113,7 +113,7 @@ export default defineComponent({
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.commandPalette>>,
|
||||
default: () => appConfig.ui.commandPalette
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div :class="ui.wrapper">
|
||||
<div :class="wrapperClass" v-bind="attrs">
|
||||
<slot name="prev" :on-click="onClickPrev">
|
||||
<UButton
|
||||
v-if="prevButton"
|
||||
@@ -40,8 +40,10 @@
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import UButton from '../elements/Button.vue'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import type { Button } from '../../types/button'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
@@ -54,6 +56,7 @@ export default defineComponent({
|
||||
components: {
|
||||
UButton
|
||||
},
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Number,
|
||||
@@ -103,15 +106,15 @@ export default defineComponent({
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.pagination>>,
|
||||
default: () => appConfig.ui.pagination
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
setup (props, { emit }) {
|
||||
setup (props, { attrs, emit }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.pagination>>(() => defu({}, props.ui, appConfig.ui.pagination))
|
||||
const ui = computed<Partial<typeof appConfig.ui.pagination>>(() => defuTwMerge({}, props.ui, appConfig.ui.pagination))
|
||||
|
||||
const currentPage = computed({
|
||||
get () {
|
||||
@@ -177,6 +180,8 @@ export default defineComponent({
|
||||
const canGoPrev = computed(() => currentPage.value > 1)
|
||||
const canGoNext = computed(() => currentPage.value < pages.value.length)
|
||||
|
||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
||||
|
||||
function onClickPage (page: number | string) {
|
||||
if (typeof page === 'string') {
|
||||
return
|
||||
@@ -202,6 +207,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui,
|
||||
currentPage,
|
||||
@@ -209,6 +215,7 @@ export default defineComponent({
|
||||
displayedPages,
|
||||
canGoPrev,
|
||||
canGoNext,
|
||||
wrapperClass,
|
||||
onClickPrev,
|
||||
onClickNext,
|
||||
onClickPage
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
<template>
|
||||
<HTabGroup :vertical="orientation === 'vertical'" :selected-index="selectedIndex" as="div" :class="ui.wrapper" @change="onChange">
|
||||
<HTabGroup
|
||||
:vertical="orientation === 'vertical'"
|
||||
:selected-index="selectedIndex"
|
||||
as="div"
|
||||
:class="wrapperClass"
|
||||
v-bind="attrs"
|
||||
@change="onChange"
|
||||
>
|
||||
<HTabList
|
||||
ref="listRef"
|
||||
:class="[ui.list.base, ui.list.background, ui.list.rounded, ui.list.shadow, ui.list.padding, ui.list.width, orientation === 'horizontal' && ui.list.height, orientation === 'horizontal' && 'inline-grid items-center']"
|
||||
@@ -45,7 +52,9 @@ import { ref, computed, watch, onMounted, defineComponent } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { TabGroup as HTabGroup, TabList as HTabList, Tab as HTab, TabPanels as HTabPanels, TabPanel as HTabPanel } from '@headlessui/vue'
|
||||
import { useResizeObserver } from '@vueuse/core'
|
||||
import { defu } from 'defu'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import type { TabItem } from '../../types/tabs'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
@@ -62,6 +71,7 @@ export default defineComponent({
|
||||
HTabPanels,
|
||||
HTabPanel
|
||||
},
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Number,
|
||||
@@ -82,15 +92,15 @@ export default defineComponent({
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.tabs>>,
|
||||
default: () => appConfig.ui.tabs
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
emits: ['update:modelValue', 'change'],
|
||||
setup (props, { emit }) {
|
||||
setup (props, { attrs, emit }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.tabs>>(() => defu({}, props.ui, appConfig.ui.tabs))
|
||||
const ui = computed<Partial<typeof appConfig.ui.tabs>>(() => defuTwMerge({}, props.ui, appConfig.ui.tabs))
|
||||
|
||||
const listRef = ref<HTMLElement>()
|
||||
const itemRefs = ref<HTMLElement[]>([])
|
||||
@@ -98,6 +108,8 @@ export default defineComponent({
|
||||
|
||||
const selectedIndex = ref(props.modelValue || props.defaultIndex)
|
||||
|
||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
||||
|
||||
// Methods
|
||||
|
||||
function calcMarkerSize (index: number) {
|
||||
@@ -137,12 +149,14 @@ export default defineComponent({
|
||||
onMounted(() => calcMarkerSize(selectedIndex.value))
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui,
|
||||
listRef,
|
||||
itemRefs,
|
||||
markerRef,
|
||||
selectedIndex,
|
||||
wrapperClass,
|
||||
onChange
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<nav :class="ui.wrapper">
|
||||
<nav :class="wrapperClass" v-bind="attrs">
|
||||
<ULink
|
||||
v-for="(link, index) of links"
|
||||
v-slot="{ isActive }"
|
||||
@@ -40,11 +40,12 @@
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import UIcon from '../elements/Icon.vue'
|
||||
import UAvatar from '../elements/Avatar.vue'
|
||||
import ULink from '../elements/Link.vue'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import type { VerticalNavigationLink } from '../../types/vertical-navigation'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
@@ -59,6 +60,7 @@ export default defineComponent({
|
||||
UAvatar,
|
||||
ULink
|
||||
},
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
links: {
|
||||
type: Array as PropType<VerticalNavigationLink[]>,
|
||||
@@ -66,18 +68,22 @@ export default defineComponent({
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.verticalNavigation>>,
|
||||
default: () => appConfig.ui.verticalNavigation
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
setup (props, { attrs }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.verticalNavigation>>(() => defu({}, props.ui, appConfig.ui.verticalNavigation))
|
||||
const ui = computed<Partial<typeof appConfig.ui.verticalNavigation>>(() => defuTwMerge({}, props.ui, appConfig.ui.verticalNavigation))
|
||||
|
||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui,
|
||||
wrapperClass,
|
||||
omit
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div v-if="isOpen" ref="container" :class="[ui.container, ui.width]">
|
||||
<div v-if="isOpen" ref="container" :class="wrapperClass" v-bind="attrs">
|
||||
<Transition appear v-bind="ui.transition">
|
||||
<div :class="[ui.base, ui.ring, ui.rounded, ui.shadow, ui.background]">
|
||||
<slot />
|
||||
@@ -14,7 +14,10 @@ import type { PropType, Ref } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { onClickOutside } from '@vueuse/core'
|
||||
import type { VirtualElement } from '@popperjs/core'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge, twJoin } from 'tailwind-merge'
|
||||
import { usePopper } from '../../composables/usePopper'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import type { PopperOptions } from '../../types'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
@@ -24,6 +27,7 @@ import appConfig from '#build/app.config'
|
||||
// const appConfig = useAppConfig()
|
||||
|
||||
export default defineComponent({
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
@@ -39,15 +43,15 @@ export default defineComponent({
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.contextMenu>>,
|
||||
default: () => appConfig.ui.contextMenu
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
emits: ['update:modelValue', 'close'],
|
||||
setup (props, { emit }) {
|
||||
setup (props, { attrs, emit }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.contextMenu>>(() => defu({}, props.ui, appConfig.ui.contextMenu))
|
||||
const ui = computed<Partial<typeof appConfig.ui.contextMenu>>(() => defuTwMerge({}, props.ui, appConfig.ui.contextMenu))
|
||||
|
||||
const popper = computed<PopperOptions>(() => defu({}, props.popper, ui.value.popper as PopperOptions))
|
||||
|
||||
@@ -64,14 +68,23 @@ export default defineComponent({
|
||||
|
||||
const [, container] = usePopper(popper.value, virtualElement)
|
||||
|
||||
const wrapperClass = computed(() => {
|
||||
return twMerge(twJoin(
|
||||
ui.value.container,
|
||||
ui.value.width
|
||||
), attrs.class as string)
|
||||
})
|
||||
|
||||
onClickOutside(container, () => {
|
||||
isOpen.value = false
|
||||
})
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui,
|
||||
isOpen,
|
||||
wrapperClass,
|
||||
container
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<TransitionRoot :appear="appear" :show="isOpen" as="template">
|
||||
<HDialog :class="ui.wrapper" @close="(e) => !preventClose && close(e)">
|
||||
<HDialog :class="wrapperClass" v-bind="attrs" @close="(e) => !preventClose && close(e)">
|
||||
<TransitionChild v-if="overlay" as="template" :appear="appear" v-bind="ui.overlay.transition">
|
||||
<div :class="[ui.overlay.base, ui.overlay.background]" />
|
||||
</TransitionChild>
|
||||
@@ -32,8 +32,10 @@
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { Dialog as HDialog, DialogPanel as HDialogPanel, TransitionRoot, TransitionChild } from '@headlessui/vue'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
// @ts-expect-error
|
||||
@@ -48,6 +50,7 @@ export default defineComponent({
|
||||
TransitionRoot,
|
||||
TransitionChild
|
||||
},
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
@@ -75,15 +78,15 @@ export default defineComponent({
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.modal>>,
|
||||
default: () => appConfig.ui.modal
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
emits: ['update:modelValue', 'close'],
|
||||
setup (props, { emit }) {
|
||||
setup (props, { attrs, emit }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.modal>>(() => defu({}, props.ui, appConfig.ui.modal))
|
||||
const ui = computed<Partial<typeof appConfig.ui.modal>>(() => defuTwMerge({}, props.ui, appConfig.ui.modal))
|
||||
|
||||
const isOpen = computed({
|
||||
get () {
|
||||
@@ -94,6 +97,8 @@ export default defineComponent({
|
||||
}
|
||||
})
|
||||
|
||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
||||
|
||||
const transitionClass = computed(() => {
|
||||
if (!props.transition) {
|
||||
return {}
|
||||
@@ -111,9 +116,11 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui,
|
||||
isOpen,
|
||||
wrapperClass,
|
||||
transitionClass,
|
||||
close
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<Transition appear v-bind="ui.transition">
|
||||
<div :class="[ui.wrapper, ui.background, ui.rounded, ui.shadow]" @mouseover="onMouseover" @mouseleave="onMouseleave">
|
||||
<div :class="wrapperClass" v-bind="attrs" @mouseover="onMouseover" @mouseleave="onMouseleave">
|
||||
<div :class="[ui.container, ui.rounded, ui.ring]">
|
||||
<div :class="ui.padding">
|
||||
<div class="flex gap-3" :class="{ 'items-start': description || $slots.description, 'items-center': !description && !$slots.description }">
|
||||
@@ -41,7 +41,8 @@
|
||||
<script lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted, watchEffect, defineComponent } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge, twJoin } from 'tailwind-merge'
|
||||
import UIcon from '../elements/Icon.vue'
|
||||
import UAvatar from '../elements/Avatar.vue'
|
||||
import UButton from '../elements/Button.vue'
|
||||
@@ -49,7 +50,7 @@ import { useTimer } from '../../composables/useTimer'
|
||||
import type { NotificationAction } from '../../types'
|
||||
import type { Avatar } from '../../types/avatar'
|
||||
import type { Button } from '../../types/button'
|
||||
import { classNames } from '../../utils'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
// @ts-expect-error
|
||||
@@ -63,6 +64,7 @@ export default defineComponent({
|
||||
UAvatar,
|
||||
UButton
|
||||
},
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
id: {
|
||||
type: [String, Number],
|
||||
@@ -109,34 +111,43 @@ export default defineComponent({
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.notification>>,
|
||||
default: () => appConfig.ui.notification
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
emits: ['close'],
|
||||
setup (props, { emit }) {
|
||||
setup (props, { attrs, emit }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.notification>>(() => defu({}, props.ui, appConfig.ui.notification))
|
||||
const ui = computed<Partial<typeof appConfig.ui.notification>>(() => defuTwMerge({}, props.ui, appConfig.ui.notification))
|
||||
|
||||
let timer: any = null
|
||||
const remaining = ref(props.timeout)
|
||||
|
||||
const wrapperClass = computed(() => {
|
||||
return twMerge(twJoin(
|
||||
ui.value.wrapper,
|
||||
ui.value.background,
|
||||
ui.value.rounded,
|
||||
ui.value.shadow
|
||||
), attrs.class as string)
|
||||
})
|
||||
|
||||
const progressClass = computed(() => {
|
||||
return twJoin(
|
||||
ui.value.progress.base,
|
||||
ui.value.progress.background?.replaceAll('{color}', props.color)
|
||||
)
|
||||
})
|
||||
|
||||
const progressStyle = computed(() => {
|
||||
const remainingPercent = remaining.value / props.timeout * 100
|
||||
|
||||
return { width: `${remainingPercent || 0}%` }
|
||||
})
|
||||
|
||||
const progressClass = computed(() => {
|
||||
return classNames(
|
||||
ui.value.progress.base,
|
||||
ui.value.progress.background?.replaceAll('{color}', props.color)
|
||||
)
|
||||
})
|
||||
|
||||
const iconClass = computed(() => {
|
||||
return classNames(
|
||||
return twJoin(
|
||||
ui.value.icon.base,
|
||||
ui.value.icon.color?.replaceAll('{color}', props.color)
|
||||
)
|
||||
@@ -199,10 +210,12 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui,
|
||||
progressStyle,
|
||||
wrapperClass,
|
||||
progressClass,
|
||||
progressStyle,
|
||||
iconClass,
|
||||
onMouseover,
|
||||
onMouseleave,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div :class="[ui.wrapper, ui.position, ui.width]">
|
||||
<div :class="wrapperClass" v-bind="attrs">
|
||||
<div v-if="notifications.length" :class="ui.container">
|
||||
<div v-for="notification of notifications" :key="notification.id">
|
||||
<UNotification
|
||||
@@ -20,10 +20,12 @@
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import type { Notification } from '../../types'
|
||||
import { useToast } from '../../composables/useToast'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge, twJoin } from 'tailwind-merge'
|
||||
import UNotification from './Notification.vue'
|
||||
import { useToast } from '../../composables/useToast'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import type { Notification } from '../../types'
|
||||
import { useState, useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
// @ts-expect-error
|
||||
@@ -35,26 +37,37 @@ export default defineComponent({
|
||||
components: {
|
||||
UNotification
|
||||
},
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.notifications>>,
|
||||
default: () => appConfig.ui.notifications
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
setup (props, { attrs }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.notifications>>(() => defu({}, props.ui, appConfig.ui.notifications))
|
||||
const ui = computed<Partial<typeof appConfig.ui.notifications>>(() => defuTwMerge({}, props.ui, appConfig.ui.notifications))
|
||||
|
||||
const toast = useToast()
|
||||
const notifications = useState<Notification[]>('notifications', () => [])
|
||||
|
||||
const wrapperClass = computed(() => {
|
||||
return twMerge(twJoin(
|
||||
ui.value.wrapper,
|
||||
ui.value.position,
|
||||
ui.value.width
|
||||
), attrs.class as string)
|
||||
})
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui,
|
||||
toast,
|
||||
notifications
|
||||
notifications,
|
||||
wrapperClass
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<HPopover ref="popover" v-slot="{ open, close }" :class="ui.wrapper" @mouseleave="onMouseLeave">
|
||||
<HPopover ref="popover" v-slot="{ open, close }" :class="wrapperClass" v-bind="attrs" @mouseleave="onMouseLeave">
|
||||
<HPopoverButton
|
||||
ref="trigger"
|
||||
as="div"
|
||||
@@ -29,8 +29,11 @@
|
||||
import { computed, ref, onMounted, defineComponent } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { Popover as HPopover, PopoverButton as HPopoverButton, PopoverPanel as HPopoverPanel } from '@headlessui/vue'
|
||||
import { usePopper } from '../../composables/usePopper'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import type { PopperOptions } from '../../types'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
@@ -45,6 +48,7 @@ export default defineComponent({
|
||||
HPopoverButton,
|
||||
HPopoverPanel
|
||||
},
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
mode: {
|
||||
type: String,
|
||||
@@ -71,14 +75,14 @@ export default defineComponent({
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.popover>>,
|
||||
default: () => appConfig.ui.popover
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
setup (props, { attrs }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.popover>>(() => defu({}, props.ui, appConfig.ui.popover))
|
||||
const ui = computed<Partial<typeof appConfig.ui.popover>>(() => defuTwMerge({}, props.ui, appConfig.ui.popover))
|
||||
|
||||
const popper = computed<PopperOptions>(() => defu(props.mode === 'hover' ? { offsetDistance: 0 } : {}, props.popper, ui.value.popper as PopperOptions))
|
||||
|
||||
@@ -106,6 +110,8 @@ export default defineComponent({
|
||||
return props.mode === 'hover' ? { paddingTop: `${offsetDistance}px`, paddingBottom: `${offsetDistance}px` } : {}
|
||||
})
|
||||
|
||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
||||
|
||||
function onMouseOver () {
|
||||
if (props.mode !== 'hover' || !popoverApi.value) {
|
||||
return
|
||||
@@ -147,12 +153,14 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui,
|
||||
popover,
|
||||
trigger,
|
||||
container,
|
||||
containerStyle,
|
||||
wrapperClass,
|
||||
onMouseOver,
|
||||
onMouseLeave
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<TransitionRoot as="template" :appear="appear" :show="isOpen">
|
||||
<HDialog :class="[ui.wrapper, { 'justify-end': side === 'right' }]" @close="(e) => !preventClose && close(e)">
|
||||
<HDialog :class="[wrapperClass, { 'justify-end': side === 'right' }]" v-bind="attrs" @close="(e) => !preventClose && close(e)">
|
||||
<TransitionChild v-if="overlay" as="template" :appear="appear" v-bind="ui.overlay.transition">
|
||||
<div :class="[ui.overlay.base, ui.overlay.background]" />
|
||||
</TransitionChild>
|
||||
@@ -17,8 +17,10 @@
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import type { WritableComputedRef, PropType } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { Dialog as HDialog, DialogPanel as HDialogPanel, TransitionRoot, TransitionChild } from '@headlessui/vue'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
// @ts-expect-error
|
||||
@@ -33,6 +35,7 @@ export default defineComponent({
|
||||
TransitionRoot,
|
||||
TransitionChild
|
||||
},
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
@@ -61,15 +64,15 @@ export default defineComponent({
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.slideover>>,
|
||||
default: () => appConfig.ui.slideover
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
emits: ['update:modelValue', 'close'],
|
||||
setup (props, { emit }) {
|
||||
setup (props, { attrs, emit }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.slideover>>(() => defu({}, props.ui, appConfig.ui.slideover))
|
||||
const ui = computed<Partial<typeof appConfig.ui.slideover>>(() => defuTwMerge({}, props.ui, appConfig.ui.slideover))
|
||||
|
||||
const isOpen: WritableComputedRef<boolean> = computed({
|
||||
get () {
|
||||
@@ -80,6 +83,8 @@ export default defineComponent({
|
||||
}
|
||||
})
|
||||
|
||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
||||
|
||||
const transitionClass = computed(() => {
|
||||
if (!props.transition) {
|
||||
return {}
|
||||
@@ -100,9 +105,11 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui,
|
||||
isOpen,
|
||||
wrapperClass,
|
||||
transitionClass,
|
||||
close
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div ref="trigger" :class="ui.wrapper" @mouseover="onMouseOver" @mouseleave="onMouseLeave">
|
||||
<div ref="trigger" :class="wrapperClass" v-bind="attrs" @mouseover="onMouseOver" @mouseleave="onMouseLeave">
|
||||
<slot :open="open">
|
||||
Hover
|
||||
</slot>
|
||||
@@ -27,8 +27,11 @@
|
||||
import { computed, ref, defineComponent } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { defu } from 'defu'
|
||||
import { omit } from 'lodash-es'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import UKbd from '../elements/Kbd.vue'
|
||||
import { usePopper } from '../../composables/usePopper'
|
||||
import { defuTwMerge } from '../../utils'
|
||||
import type { PopperOptions } from '../../types'
|
||||
import { useAppConfig } from '#imports'
|
||||
// TODO: Remove
|
||||
@@ -41,6 +44,7 @@ export default defineComponent({
|
||||
components: {
|
||||
UKbd
|
||||
},
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
text: {
|
||||
type: String,
|
||||
@@ -68,14 +72,14 @@ export default defineComponent({
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<Partial<typeof appConfig.ui.tooltip>>,
|
||||
default: () => appConfig.ui.tooltip
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
setup (props, { attrs }) {
|
||||
// TODO: Remove
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const ui = computed<Partial<typeof appConfig.ui.tooltip>>(() => defu({}, props.ui, appConfig.ui.tooltip))
|
||||
const ui = computed<Partial<typeof appConfig.ui.tooltip>>(() => defuTwMerge({}, props.ui, appConfig.ui.tooltip))
|
||||
|
||||
const popper = computed<PopperOptions>(() => defu({}, props.popper, ui.value.popper as PopperOptions))
|
||||
|
||||
@@ -86,6 +90,8 @@ export default defineComponent({
|
||||
let openTimeout: NodeJS.Timeout | null = null
|
||||
let closeTimeout: NodeJS.Timeout | null = null
|
||||
|
||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
||||
|
||||
// Methods
|
||||
|
||||
function onMouseOver () {
|
||||
@@ -121,11 +127,13 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
return {
|
||||
attrs: omit(attrs, ['class']),
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui,
|
||||
trigger,
|
||||
container,
|
||||
open,
|
||||
wrapperClass,
|
||||
onMouseOver,
|
||||
onMouseLeave
|
||||
}
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
export function classNames (...classes: any[string]) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
import { createDefu } from 'defu'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
export const defuTwMerge = createDefu((obj, key, value) => {
|
||||
if (typeof obj[key] === 'string' && typeof value === 'string' && obj[key] && value) {
|
||||
// @ts-ignore
|
||||
obj[key] = twMerge(obj[key], value)
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
export const hexToRgb = (hex: string) => {
|
||||
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
|
||||
|
||||
Reference in New Issue
Block a user