mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-14 12:14:41 +01:00
feat: migrate to @nuxtjs/tailwindcss (#32)
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# @nuxthq/ui
|
# @nuxthq/ui
|
||||||
|
|
||||||
Components library as a Nuxt3 module using [UnoCSS](https://github.com/antfu/unocss).
|
Components library as a Nuxt3 module using [TailwindCSS](https://tailwindcss.com).
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|||||||
13
docs/app.vue
13
docs/app.vue
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="antialiased font-sans">
|
<div class="antialiased font-sans">
|
||||||
<nav class="u-bg-white border-b u-border-gray-200 fixed top-0 inset-x-0 z-50">
|
<nav class="u-bg-white border-b u-border-gray-200 fixed top-0 inset-x-0 z-20">
|
||||||
<UContainer padded>
|
<UContainer padded>
|
||||||
<div class="flex items-center justify-between h-16">
|
<div class="flex items-center justify-between h-16">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
@@ -62,16 +62,9 @@ const sections = [
|
|||||||
{ label: 'Getting Started', links: [{ label: 'Usage', to: '/' }, { label: 'Examples', to: '/examples' }, { label: 'Migration', to: '/migration' }, { label: 'Dark mode', to: '/dark' }] },
|
{ label: 'Getting Started', links: [{ label: 'Usage', to: '/' }, { label: 'Examples', to: '/examples' }, { label: 'Migration', to: '/migration' }, { label: 'Dark mode', to: '/dark' }] },
|
||||||
{ label: 'Elements', links: [{ label: 'Avatar', to: '/components/Avatar' }, { label: 'AvatarGroup', to: '/components/AvatarGroup' }, { label: 'Badge', to: '/components/Badge' }, { label: 'Button', to: '/components/Button' }, { label: 'Dropdown', to: '/components/Dropdown' }, { label: 'Icon', to: '/components/Icon' }] },
|
{ label: 'Elements', links: [{ label: 'Avatar', to: '/components/Avatar' }, { label: 'AvatarGroup', to: '/components/AvatarGroup' }, { label: 'Badge', to: '/components/Badge' }, { label: 'Button', to: '/components/Button' }, { label: 'Dropdown', to: '/components/Dropdown' }, { label: 'Icon', to: '/components/Icon' }] },
|
||||||
{ label: 'Feedback', links: [{ label: 'Alert', to: '/components/Alert' }, { label: 'AlertDialog', to: '/components/AlertDialog' }] },
|
{ label: 'Feedback', links: [{ label: 'Alert', to: '/components/Alert' }, { label: 'AlertDialog', to: '/components/AlertDialog' }] },
|
||||||
{ label: 'Forms', links: [{ label: 'Checkbox', to: '/components/Checkbox' }, { label: 'Input', to: '/components/Input' }, { label: 'FormGroup', to: '/components/FormGroup' }, { label: 'Radio', to: '/components/Radio' }, { label: 'Select', to: '/components/Select' }, { label: 'Textarea', to: '/components/Textarea' }, { label: 'Toggle', to: '/components/Toggle' }] },
|
{ label: 'Forms', links: [{ label: 'Checkbox', to: '/components/Checkbox' }, { label: 'Input', to: '/components/Input' }, { label: 'FormGroup', to: '/components/FormGroup' }, { label: 'Radio', to: '/components/Radio' }, { label: 'Select', to: '/components/Select' }, { label: 'SelectCustom', to: '/components/SelectCustom' }, { label: 'Textarea', to: '/components/Textarea' }, { label: 'Toggle', to: '/components/Toggle' }] },
|
||||||
{ label: 'Layout', links: [{ label: 'Card', to: '/components/Card' }, { label: 'Container', to: '/components/Container' }] },
|
{ label: 'Layout', links: [{ label: 'Card', to: '/components/Card' }, { label: 'Container', to: '/components/Container' }] },
|
||||||
// { label: 'Navigation', links: [{ label: 'Pills', to: '/components/Pills' }, { label: 'Tabs', to: '/components/Tabs' }, { label: 'VerticalNavigation', to: '/components/VerticalNavigation' }] },
|
{ label: 'Navigation', links: [{ label: 'Pills', to: '/components/Pills' }, { label: 'Tabs', to: '/components/Tabs' }, { label: 'VerticalNavigation', to: '/components/VerticalNavigation' }] },
|
||||||
{ label: 'Navigation', links: [{ label: 'VerticalNavigation', to: '/components/VerticalNavigation' }] },
|
|
||||||
{ label: 'Overlays', links: [{ label: 'Modal', to: '/components/Modal' }, { label: 'Notification', to: '/components/Notification' }, { label: 'Popover', to: '/components/Popover' }, { label: 'Tooltip', to: '/components/Tooltip' }] }
|
{ label: 'Overlays', links: [{ label: 'Modal', to: '/components/Modal' }, { label: 'Notification', to: '/components/Notification' }, { label: 'Popover', to: '/components/Popover' }, { label: 'Tooltip', to: '/components/Tooltip' }] }
|
||||||
]
|
]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
|
||||||
html.dark {
|
|
||||||
background-color: black;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -28,17 +28,13 @@ export default defineNuxtConfig({
|
|||||||
primary: 'blue'
|
primary: 'blue'
|
||||||
},
|
},
|
||||||
preset: {
|
preset: {
|
||||||
container: {
|
|
||||||
constrained: 'max-w-8xl'
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
unocss: {
|
tailwindcss: {
|
||||||
theme: {
|
theme: {
|
||||||
fontFamily: {
|
extend: {
|
||||||
sans: '"Inter var", sans-serif'
|
fontFamily: {
|
||||||
},
|
sans: '"Inter var", sans-serif'
|
||||||
maxWidth: {
|
}
|
||||||
'8xl': '90rem'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,6 +100,15 @@ const is = `U${params.component}`
|
|||||||
|
|
||||||
const component = nuxtApp.vueApp.component(is)
|
const component = nuxtApp.vueApp.component(is)
|
||||||
|
|
||||||
|
const people = [
|
||||||
|
{ id: 1, name: 'Durward Reynolds', disabled: false },
|
||||||
|
{ id: 2, name: 'Kenton Towne', disabled: false },
|
||||||
|
{ id: 3, name: 'Therese Wunsch', disabled: false },
|
||||||
|
{ id: 4, name: 'Benedict Kessler', disabled: true },
|
||||||
|
{ id: 5, name: 'Katelyn Rohan', disabled: false }
|
||||||
|
]
|
||||||
|
|
||||||
|
const selectCustom = ref(people[0])
|
||||||
const alertDialog = ref(false)
|
const alertDialog = ref(false)
|
||||||
const toggle = ref(false)
|
const toggle = ref(false)
|
||||||
const modal = ref(false)
|
const modal = ref(false)
|
||||||
@@ -214,6 +223,12 @@ const defaultProps = {
|
|||||||
name: 'select',
|
name: 'select',
|
||||||
options: ['English', 'Spanish', 'French', 'German', 'Chinese']
|
options: ['English', 'Spanish', 'French', 'German', 'Chinese']
|
||||||
},
|
},
|
||||||
|
SelectCustom: {
|
||||||
|
modelValue: selectCustom,
|
||||||
|
'onUpdate:modelValue': (v) => { selectCustom.value = v },
|
||||||
|
textAttribute: 'name',
|
||||||
|
options: people
|
||||||
|
},
|
||||||
Textarea: {
|
Textarea: {
|
||||||
name: 'textarea'
|
name: 'textarea'
|
||||||
},
|
},
|
||||||
@@ -264,6 +279,10 @@ const defaultProps = {
|
|||||||
const componentDefaultProps = defaultProps[params.component] || {}
|
const componentDefaultProps = defaultProps[params.component] || {}
|
||||||
const { props: componentProps } = await component.__asyncLoader()
|
const { props: componentProps } = await component.__asyncLoader()
|
||||||
|
|
||||||
|
function lowercaseFirstLetter (string) {
|
||||||
|
return string.charAt(0).toLowerCase() + string.slice(1)
|
||||||
|
}
|
||||||
|
|
||||||
const refProps = Object.entries(componentProps).map(([key, prop]) => {
|
const refProps = Object.entries(componentProps).map(([key, prop]) => {
|
||||||
const defaultValue = componentDefaultProps[key]
|
const defaultValue = componentDefaultProps[key]
|
||||||
const propDefault = (typeof prop.default === 'function' ? prop.default() : prop.default)
|
const propDefault = (typeof prop.default === 'function' ? prop.default() : prop.default)
|
||||||
@@ -281,7 +300,7 @@ const refProps = Object.entries(componentProps).map(([key, prop]) => {
|
|||||||
if (arrayRegex) {
|
if (arrayRegex) {
|
||||||
values = JSON.parse(arrayRegex[0].replace(/'/g, '"')).filter(Boolean)
|
values = JSON.parse(arrayRegex[0].replace(/'/g, '"')).filter(Boolean)
|
||||||
} else {
|
} else {
|
||||||
const $uiProp = $ui[params.component.toLowerCase()][key]
|
const $uiProp = $ui[lowercaseFirstLetter(params.component)][key]
|
||||||
if ($uiProp) {
|
if ($uiProp) {
|
||||||
values = Object.keys($uiProp).filter(Boolean)
|
values = Object.keys($uiProp).filter(Boolean)
|
||||||
}
|
}
|
||||||
@@ -289,7 +308,7 @@ const refProps = Object.entries(componentProps).map(([key, prop]) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
if (type === 'String') {
|
if (type === 'String' && typeof value === 'string') {
|
||||||
value = value.replace(/^'(.*)'$/, '$1')
|
value = value.replace(/^'(.*)'$/, '$1')
|
||||||
} else if (type === 'Array') {
|
} else if (type === 'Array') {
|
||||||
value = JSON.stringify(value)
|
value = JSON.stringify(value)
|
||||||
@@ -307,6 +326,7 @@ const refProps = Object.entries(componentProps).map(([key, prop]) => {
|
|||||||
|
|
||||||
const eventProps = Object.entries(componentDefaultProps)
|
const eventProps = Object.entries(componentDefaultProps)
|
||||||
.filter(([key]) => !refProps.find(prop => prop.key === key))
|
.filter(([key]) => !refProps.find(prop => prop.key === key))
|
||||||
|
.filter(([key]) => !['slots'].includes(key))
|
||||||
.reduce((acc, [key, value]) => {
|
.reduce((acc, [key, value]) => {
|
||||||
acc[key] = value
|
acc[key] = value
|
||||||
return acc
|
return acc
|
||||||
|
|||||||
@@ -178,13 +178,28 @@
|
|||||||
<UCard body-class="flex" @submit.prevent="onSubmit">
|
<UCard body-class="flex" @submit.prevent="onSubmit">
|
||||||
<div class="flex-1 px-4 py-5 sm:p-6 space-y-3">
|
<div class="flex-1 px-4 py-5 sm:p-6 space-y-3">
|
||||||
<UFormGroup label="Email" name="email" required>
|
<UFormGroup label="Email" name="email" required>
|
||||||
<UInput v-model="form.email" type="email" name="email" required />
|
<UInput v-model="form.email" type="email" name="email" required icon="heroicons-outline:mail" />
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
<UFormGroup label="Description" name="description">
|
<UFormGroup label="Description" name="description">
|
||||||
<UTextarea v-model="form.description" type="description" name="description" autoresize />
|
<UTextarea v-model="form.description" type="description" name="description" autoresize />
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Person" name="person" required>
|
||||||
|
<USelect
|
||||||
|
v-model="form.personId"
|
||||||
|
name="person"
|
||||||
|
:options="people"
|
||||||
|
text-attribute="name"
|
||||||
|
value-attribute="id"
|
||||||
|
icon="heroicons-outline:user"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="People" name="people" required>
|
||||||
|
<USelectCustom v-model="form.person" name="people" :options="people" text-attribute="name" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
<UFormGroup label="Toggle" name="toggle">
|
<UFormGroup label="Toggle" name="toggle">
|
||||||
<UToggle v-model="form.toggle" name="toggle" icon-off="heroicons-solid:x" icon-on="heroicons-solid:check" />
|
<UToggle v-model="form.toggle" name="toggle" icon-off="heroicons-solid:x" icon-on="heroicons-solid:check" />
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
@@ -223,13 +238,23 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
const isModalOpen = ref(false)
|
const isModalOpen = ref(false)
|
||||||
|
|
||||||
|
const people = [
|
||||||
|
{ id: 1, name: 'Durward Reynolds', disabled: false },
|
||||||
|
{ id: 2, name: 'Kenton Towne', disabled: false },
|
||||||
|
{ id: 3, name: 'Therese Wunsch', disabled: false },
|
||||||
|
{ id: 4, name: 'Benedict Kessler', disabled: true },
|
||||||
|
{ id: 5, name: 'Katelyn Rohan', disabled: false }
|
||||||
|
]
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
email: '',
|
email: '',
|
||||||
description: '',
|
description: '',
|
||||||
toggle: false,
|
toggle: false,
|
||||||
notification: 'email',
|
notification: 'email',
|
||||||
notifications: [],
|
notifications: [],
|
||||||
terms: false
|
terms: false,
|
||||||
|
personId: people[0].id,
|
||||||
|
person: ref(people[0])
|
||||||
})
|
})
|
||||||
|
|
||||||
const { $toast } = useNuxtApp()
|
const { $toast } = useNuxtApp()
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="mt-1 text-lg u-text-gray-500">
|
<p class="mt-1 text-lg u-text-gray-500">
|
||||||
Components library as a Nuxt3 module using <a href="https://github.com/antfu/unocss" target="_blank" class="underline">UnoCSS</a>.
|
Components library as a Nuxt3 module using <a href="https://tailwindcss.com" target="_blank" class="underline">TailwindCSS</a>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -66,23 +66,11 @@
|
|||||||
|
|
||||||
<p>- `colors.gray`</p>
|
<p>- `colors.gray`</p>
|
||||||
|
|
||||||
<p>Define the gray variant. Defaults to `zinc`. You can like the `primary` color specify your own object. https://github.com/antfu/unocss/blob/main/packages/preset-uno/src/theme/colors.ts</p>
|
<p>Define the gray variant. Defaults to `zinc`. You can like the `primary` color specify your own object. https://tailwindcss.com/docs/customizing-colors#default-color-palette</p>
|
||||||
|
|
||||||
<p>- `unocss.shortcuts`. Defaults to `[]`.</p>
|
<p>- `tailwindcss.theme`. Defaults to `{}`.</p>
|
||||||
|
|
||||||
<p>Define UnoCSS shortcuts: https://github.com/antfu/unocss#shortcuts.</p>
|
<p>Define TailwindCSS theme: https://tailwindcss.com/docs/theme.</p>
|
||||||
|
|
||||||
<p>- `unocss.rules`. Defaults to `[]`.</p>
|
|
||||||
|
|
||||||
<p>Customize UnoCSS rules: https://github.com/antfu/unocss#custom-rules.</p>
|
|
||||||
|
|
||||||
<p>- `unocss.variants`. Defaults to `[]`.</p>
|
|
||||||
|
|
||||||
<p>Customize UnoCSS variants: https://github.com/antfu/unocss#custom-variants.</p>
|
|
||||||
|
|
||||||
<p>- `unocss.theme`. Defaults to `{}`.</p>
|
|
||||||
|
|
||||||
<p>Extend UnoCSS theme: https://github.com/antfu/unocss#extend-theme.</p>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
10
package.json
10
package.json
@@ -29,7 +29,6 @@
|
|||||||
"@iconify-json/heroicons-solid": "^1.0.2",
|
"@iconify-json/heroicons-solid": "^1.0.2",
|
||||||
"@nuxt/kit": "npm:@nuxt/kit-edge@latest",
|
"@nuxt/kit": "npm:@nuxt/kit-edge@latest",
|
||||||
"@popperjs/core": "^2.11.2",
|
"@popperjs/core": "^2.11.2",
|
||||||
"@unocss/nuxt": "^0.24.3",
|
|
||||||
"@vueuse/core": "^7.6.1",
|
"@vueuse/core": "^7.6.1",
|
||||||
"defu": "^5.0.1",
|
"defu": "^5.0.1",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
@@ -37,8 +36,14 @@
|
|||||||
"pathe": "^0.2.0"
|
"pathe": "^0.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@iconify/vue": "^3.1.3",
|
||||||
"@nuxt/module-builder": "^0.1.7",
|
"@nuxt/module-builder": "^0.1.7",
|
||||||
"@nuxtjs/eslint-config-typescript": "^8.0.0",
|
"@nuxtjs/eslint-config-typescript": "^8.0.0",
|
||||||
|
"@nuxtjs/tailwindcss": "^5.0.0-4",
|
||||||
|
"@tailwindcss/aspect-ratio": "^0.4.0",
|
||||||
|
"@tailwindcss/forms": "^0.4.0",
|
||||||
|
"@tailwindcss/line-clamp": "^0.3.1",
|
||||||
|
"@types/tailwindcss": "^3.0.7",
|
||||||
"@types/lodash-es": "^4.17.6",
|
"@types/lodash-es": "^4.17.6",
|
||||||
"@vueuse/components": "^7.6.1",
|
"@vueuse/components": "^7.6.1",
|
||||||
"eslint": "^8.8.0",
|
"eslint": "^8.8.0",
|
||||||
@@ -47,7 +52,8 @@
|
|||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"externals": [
|
"externals": [
|
||||||
"@unocss/preset-mini"
|
"tailwindcss",
|
||||||
|
"tailwindcss/colors.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
149
src/module.ts
149
src/module.ts
@@ -1,8 +1,10 @@
|
|||||||
|
import { fileURLToPath } from 'url'
|
||||||
import { resolve } from 'pathe'
|
import { resolve } from 'pathe'
|
||||||
import { defineNuxtModule, installModule, addComponentsDir, addTemplate, addPlugin, resolveModule } from '@nuxt/kit'
|
import { defineNuxtModule, installModule, addComponentsDir, addTemplate, addPlugin, resolveModule } from '@nuxt/kit'
|
||||||
import { colors } from '@unocss/preset-mini'
|
|
||||||
import defu from 'defu'
|
import defu from 'defu'
|
||||||
import type { UnocssNuxtOptions } from '@unocss/nuxt'
|
import type { TailwindConfig } from 'tailwindcss/tailwind-config'
|
||||||
|
import colors from 'tailwindcss/colors.js'
|
||||||
|
import { name, version } from '../package.json'
|
||||||
|
|
||||||
interface ColorsOptions {
|
interface ColorsOptions {
|
||||||
/**
|
/**
|
||||||
@@ -29,7 +31,7 @@ export interface ModuleOptions {
|
|||||||
|
|
||||||
colors?: ColorsOptions
|
colors?: ColorsOptions
|
||||||
|
|
||||||
unocss?: UnocssNuxtOptions
|
tailwindcss?: TailwindConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaults = {
|
const defaults = {
|
||||||
@@ -39,17 +41,15 @@ const defaults = {
|
|||||||
primary: 'indigo',
|
primary: 'indigo',
|
||||||
gray: 'zinc'
|
gray: 'zinc'
|
||||||
},
|
},
|
||||||
unocss: {
|
tailwindcss: {
|
||||||
shortcuts: [],
|
|
||||||
rules: [],
|
|
||||||
variants: [],
|
|
||||||
theme: {}
|
theme: {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineNuxtModule<ModuleOptions>({
|
export default defineNuxtModule<ModuleOptions>({
|
||||||
meta: {
|
meta: {
|
||||||
name: '@nuxthq/ui',
|
name,
|
||||||
|
version,
|
||||||
configKey: 'ui',
|
configKey: 'ui',
|
||||||
compatibility: {
|
compatibility: {
|
||||||
nuxt: '^3.0.0',
|
nuxt: '^3.0.0',
|
||||||
@@ -57,111 +57,41 @@ export default defineNuxtModule<ModuleOptions>({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
defaults,
|
defaults,
|
||||||
async setup (_options, nuxt) {
|
async setup (options, nuxt) {
|
||||||
const { preset, prefix, colors: { primary = 'indigo', gray = 'zinc' } = {} } = _options
|
const { preset, prefix, colors: { primary = 'indigo', gray = 'zinc' } = {}, tailwindcss: { theme = {} } = {} } = options
|
||||||
const { shortcuts = [], rules = [], variants = [], theme = {} } = _options.unocss || {}
|
|
||||||
|
|
||||||
const options: UnocssNuxtOptions = {
|
|
||||||
theme: {
|
|
||||||
colors: {
|
|
||||||
gray: typeof gray === 'object' ? gray : (colors && colors[gray]),
|
|
||||||
primary: typeof primary === 'object' ? primary : (colors && colors[primary])
|
|
||||||
},
|
|
||||||
...theme
|
|
||||||
},
|
|
||||||
preflight: true,
|
|
||||||
icons: {
|
|
||||||
prefix: ''
|
|
||||||
},
|
|
||||||
shortcuts: {
|
|
||||||
[`${prefix}-bg-white`]: 'bg-white dark:bg-black',
|
|
||||||
[`${prefix}-bg-gray-50`]: 'bg-gray-50 dark:bg-gray-900',
|
|
||||||
[`${prefix}-bg-gray-100`]: 'bg-gray-100 dark:bg-gray-800',
|
|
||||||
[`${prefix}-bg-gray-200`]: 'bg-gray-200 dark:bg-gray-700',
|
|
||||||
[`${prefix}-bg-gray-300`]: 'bg-gray-300 dark:bg-gray-600',
|
|
||||||
[`${prefix}-bg-gray-400`]: 'bg-gray-400 dark:bg-gray-500',
|
|
||||||
[`${prefix}-bg-gray-500`]: 'bg-gray-500 dark:bg-gray-400',
|
|
||||||
[`${prefix}-bg-gray-600`]: 'bg-gray-600 dark:bg-gray-300',
|
|
||||||
[`${prefix}-bg-gray-700`]: 'bg-gray-700 dark:bg-gray-200',
|
|
||||||
[`${prefix}-bg-gray-800`]: 'bg-gray-800 dark:bg-gray-100',
|
|
||||||
[`${prefix}-bg-gray-900`]: 'bg-gray-900 dark:bg-gray-50',
|
|
||||||
[`${prefix}-bg-black`]: 'bg-black dark:bg-white',
|
|
||||||
[`${prefix}-text-white`]: 'text-white dark:text-black',
|
|
||||||
[`${prefix}-text-gray-50`]: 'text-gray-50 dark:text-gray-900',
|
|
||||||
[`${prefix}-text-gray-100`]: 'text-gray-100 dark:text-gray-800',
|
|
||||||
[`${prefix}-text-gray-200`]: 'text-gray-200 dark:text-gray-700',
|
|
||||||
[`${prefix}-text-gray-300`]: 'text-gray-300 dark:text-gray-600',
|
|
||||||
[`${prefix}-text-gray-400`]: 'text-gray-400 dark:text-gray-500',
|
|
||||||
[`${prefix}-text-gray-500`]: 'text-gray-500 dark:text-gray-400',
|
|
||||||
[`${prefix}-text-gray-600`]: 'text-gray-600 dark:text-gray-300',
|
|
||||||
[`${prefix}-text-gray-700`]: 'text-gray-700 dark:text-gray-200',
|
|
||||||
[`${prefix}-text-gray-800`]: 'text-gray-800 dark:text-gray-100',
|
|
||||||
[`${prefix}-text-gray-900`]: 'text-gray-900 dark:text-gray-50',
|
|
||||||
[`${prefix}-text-black`]: 'text-black dark:text-white',
|
|
||||||
[`${prefix}-border-white`]: 'border-white dark:border-black',
|
|
||||||
[`${prefix}-border-gray-100`]: 'border-gray-100 dark:border-gray-900',
|
|
||||||
[`${prefix}-border-gray-200`]: 'border-gray-200 dark:border-gray-800',
|
|
||||||
[`${prefix}-border-gray-300`]: 'border-gray-300 dark:border-gray-700',
|
|
||||||
[`${prefix}-border-gray-400`]: 'border-gray-400 dark:border-gray-600',
|
|
||||||
[`${prefix}-border-gray-500`]: 'border-gray-500 dark:border-gray-500',
|
|
||||||
[`${prefix}-border-gray-600`]: 'border-gray-600 dark:border-gray-400',
|
|
||||||
[`${prefix}-border-gray-700`]: 'border-gray-700 dark:border-gray-300',
|
|
||||||
[`${prefix}-border-gray-800`]: 'border-gray-800 dark:border-gray-200',
|
|
||||||
[`${prefix}-border-gray-900`]: 'border-gray-900 dark:border-gray-100',
|
|
||||||
[`${prefix}-border-black`]: 'border-black dark:border-white',
|
|
||||||
[`${prefix}-divide-white`]: 'divide-white dark:divide-black',
|
|
||||||
[`${prefix}-divide-gray-100`]: 'divide-gray-100 dark:divide-gray-900',
|
|
||||||
[`${prefix}-divide-gray-200`]: 'divide-gray-200 dark:divide-gray-800',
|
|
||||||
[`${prefix}-divide-gray-300`]: 'divide-gray-300 dark:divide-gray-700',
|
|
||||||
[`${prefix}-divide-gray-400`]: 'divide-gray-400 dark:divide-gray-600',
|
|
||||||
[`${prefix}-divide-gray-500`]: 'divide-gray-500 dark:divide-gray-500',
|
|
||||||
[`${prefix}-divide-gray-600`]: 'divide-gray-600 dark:divide-gray-400',
|
|
||||||
[`${prefix}-divide-gray-700`]: 'divide-gray-700 dark:divide-gray-300',
|
|
||||||
[`${prefix}-divide-gray-800`]: 'divide-gray-800 dark:divide-gray-200',
|
|
||||||
[`${prefix}-divide-gray-900`]: 'divide-gray-900 dark:divide-gray-100',
|
|
||||||
[`${prefix}-divide-black`]: 'divide-black dark:divide-white',
|
|
||||||
[`${prefix}-ring-white`]: 'ring-white dark:ring-black',
|
|
||||||
[`${prefix}-ring-gray-100`]: 'ring-gray-100 dark:ring-gray-900',
|
|
||||||
[`${prefix}-ring-gray-200`]: 'ring-gray-200 dark:ring-gray-800',
|
|
||||||
[`${prefix}-ring-gray-300`]: 'ring-gray-300 dark:ring-gray-700',
|
|
||||||
[`${prefix}-ring-gray-400`]: 'ring-gray-400 dark:ring-gray-600',
|
|
||||||
[`${prefix}-ring-gray-500`]: 'ring-gray-500 dark:ring-gray-500',
|
|
||||||
[`${prefix}-ring-gray-600`]: 'ring-gray-600 dark:ring-gray-400',
|
|
||||||
[`${prefix}-ring-gray-700`]: 'ring-gray-700 dark:ring-gray-300',
|
|
||||||
[`${prefix}-ring-gray-800`]: 'ring-gray-800 dark:ring-gray-200',
|
|
||||||
[`${prefix}-ring-gray-900`]: 'ring-gray-900 dark:ring-gray-100',
|
|
||||||
[`${prefix}-ring-black`]: 'ring-black dark:ring-white',
|
|
||||||
...shortcuts
|
|
||||||
},
|
|
||||||
rules: [
|
|
||||||
[/^shadow-?(.*)$/, ([, d], { theme }) => {
|
|
||||||
// @ts-ignore
|
|
||||||
const value = theme?.boxShadow?.[d || 'DEFAULT']
|
|
||||||
if (value) {
|
|
||||||
return {
|
|
||||||
'--un-shadow-color': '0,0,0',
|
|
||||||
'--un-shadow': value,
|
|
||||||
'box-shadow': 'var(--un-shadow)'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
...rules
|
|
||||||
],
|
|
||||||
variants,
|
|
||||||
layers: {
|
|
||||||
icons: 0,
|
|
||||||
default: 1,
|
|
||||||
shortcuts: 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await installModule('@unocss/nuxt', options, nuxt)
|
|
||||||
|
|
||||||
// Transpile runtime
|
// Transpile runtime
|
||||||
const runtimeDir = resolve(__dirname, './runtime')
|
const runtimeDir = fileURLToPath(new URL('./runtime', import.meta.url))
|
||||||
nuxt.options.build.transpile.push(runtimeDir)
|
nuxt.options.build.transpile.push(runtimeDir)
|
||||||
nuxt.options.build.transpile.push('@popperjs/core', '@headlessui/vue')
|
nuxt.options.build.transpile.push('@popperjs/core', '@headlessui/vue')
|
||||||
|
|
||||||
|
await installModule('@nuxtjs/tailwindcss', {
|
||||||
|
viewer: false,
|
||||||
|
config: {
|
||||||
|
darkMode: 'class',
|
||||||
|
theme: defu.arrayFn({
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
// @ts-ignore
|
||||||
|
gray: typeof gray === 'object' ? gray : (colors && colors[gray]),
|
||||||
|
// @ts-ignore
|
||||||
|
primary: typeof primary === 'object' ? primary : (colors && colors[primary])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, theme),
|
||||||
|
plugins: [
|
||||||
|
require('@tailwindcss/forms'),
|
||||||
|
require('@tailwindcss/line-clamp'),
|
||||||
|
require('@tailwindcss/aspect-ratio')
|
||||||
|
],
|
||||||
|
content: [
|
||||||
|
`${runtimeDir}/components/**/*.{vue,js,ts}`,
|
||||||
|
`${runtimeDir}/presets/**/*.{js,ts}`
|
||||||
|
]
|
||||||
|
},
|
||||||
|
cssPath: `${runtimeDir}/tailwind.css`
|
||||||
|
})
|
||||||
|
|
||||||
const presetsDir = resolve(runtimeDir, './presets')
|
const presetsDir = resolve(runtimeDir, './presets')
|
||||||
|
|
||||||
let ui: object = (await import(resolveModule(`./${defaults.preset}`, { paths: presetsDir }))).default
|
let ui: object = (await import(resolveModule(`./${defaults.preset}`, { paths: presetsDir }))).default
|
||||||
@@ -220,8 +150,5 @@ export default defineNuxtModule<ModuleOptions>({
|
|||||||
nuxt.hook('autoImports:dirs', (dirs) => {
|
nuxt.hook('autoImports:dirs', (dirs) => {
|
||||||
dirs.push(resolve(runtimeDir, 'composables'))
|
dirs.push(resolve(runtimeDir, 'composables'))
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add CSS
|
|
||||||
nuxt.options.css.push(resolve(runtimeDir, 'css', 'forms.css'))
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,14 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="name" />
|
<Icon :icon="name" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
import { Icon, addCollection } from '@iconify/vue/dist/offline'
|
||||||
props: {
|
import outline from '@iconify-json/heroicons-outline/icons.json'
|
||||||
name: {
|
import solid from '@iconify-json/heroicons-solid/icons.json'
|
||||||
type: String,
|
|
||||||
required: true
|
addCollection(outline)
|
||||||
}
|
addCollection(solid)
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="wrapperClass">
|
<div :class="wrapperClass">
|
||||||
<div v-if="isLeading" :class="iconLeadingWrapperClass">
|
|
||||||
<Icon :name="iconName" :class="iconClass" />
|
|
||||||
</div>
|
|
||||||
<input
|
<input
|
||||||
:id="name"
|
:id="name"
|
||||||
ref="input"
|
ref="input"
|
||||||
@@ -21,6 +18,9 @@
|
|||||||
@blur="$emit('blur', $event)"
|
@blur="$emit('blur', $event)"
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
|
<div v-if="isLeading" :class="iconLeadingWrapperClass">
|
||||||
|
<Icon :name="iconName" :class="iconClass" />
|
||||||
|
</div>
|
||||||
<div v-if="isTrailing" :class="iconTrailingWrapperClass">
|
<div v-if="isTrailing" :class="iconTrailingWrapperClass">
|
||||||
<Icon :name="iconName" :class="iconClass" />
|
<Icon :name="iconName" :class="iconClass" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="wrapperClass">
|
<div :class="wrapperClass">
|
||||||
<div v-if="icon" :class="iconWrapperClass">
|
|
||||||
<Icon :name="icon" :class="iconClass" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<select
|
<select
|
||||||
:id="name"
|
:id="name"
|
||||||
:name="name"
|
:name="name"
|
||||||
@@ -36,6 +32,10 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<div v-if="icon" :class="iconWrapperClass">
|
||||||
|
<Icon :name="icon" :class="iconClass" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -189,7 +189,7 @@ export default {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const iconWrapperClass = $ui.select.icon.leading.base
|
const iconWrapperClass = $ui.select.icon.leading.wrapper
|
||||||
|
|
||||||
return {
|
return {
|
||||||
select,
|
select,
|
||||||
|
|||||||
@@ -1,448 +1,165 @@
|
|||||||
<template>
|
<template>
|
||||||
<div ref="container">
|
<Listbox
|
||||||
<input :value="value" :required="required" class="absolute inset-0 w-px opacity-0 cursor-default">
|
:model-value="modelValue"
|
||||||
|
as="div"
|
||||||
|
:class="wrapperClass"
|
||||||
|
@update:model-value="$emit('update:modelValue', $event)"
|
||||||
|
>
|
||||||
|
<ListboxButton :class="selectCustomClass">
|
||||||
|
<span class="block truncate">{{ modelValue[textAttribute] }}</span>
|
||||||
|
<span :class="iconWrapperClass">
|
||||||
|
<Icon name="heroicons-solid:selector" :class="iconClass" aria-hidden="true" />
|
||||||
|
</span>
|
||||||
|
</ListboxButton>
|
||||||
|
|
||||||
<slot :toggle="toggle" :open="open">
|
<transition leave-active-class="transition ease-in duration-100" leave-from-class="opacity-100" leave-to-class="opacity-0">
|
||||||
<TwButton
|
<ListboxOptions class="absolute z-10 mt-1 w-full bg-white shadow-lg max-h-60 rounded-md py-1 text-base ring-1 u-ring-gray-200 overflow-auto focus:outline-none sm:text-sm">
|
||||||
icon="solid/selector"
|
<ListboxOption
|
||||||
icon-class="u-text-gray-400"
|
v-for="(option, index) in options"
|
||||||
trailing
|
v-slot="{ active, selected, disabled }"
|
||||||
:size="size"
|
:key="index"
|
||||||
:variant="variant"
|
as="template"
|
||||||
base-class="w-full cursor-default focus:outline-none disabled:cursor-not-allowed disabled:opacity-75"
|
:value="option"
|
||||||
:disabled="disabled || !options || !options.length"
|
:disabled="option.disabled"
|
||||||
@click.native="!disabled && options && options.length && toggle()"
|
|
||||||
>
|
|
||||||
<div v-if="selectedOptions && selectedOptions.length" class="inline-flex w-full px-3 py-2 -my-2 -ml-3 truncate">
|
|
||||||
<span v-for="(selectedOption, index) of selectedOptions" :key="index" class="inline-flex items-center pr-2">
|
|
||||||
<slot name="label" :option="selectedOption">
|
|
||||||
<span class="u-text-gray-700">{{ selectedOption[textAttribute] }}</span>
|
|
||||||
</slot>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div v-else class="inline-flex w-full u-text-gray-400">
|
|
||||||
{{ placeholder || '' }}
|
|
||||||
</div>
|
|
||||||
</TwButton>
|
|
||||||
</slot>
|
|
||||||
|
|
||||||
<transition
|
|
||||||
enter-class=""
|
|
||||||
enter-active-class=""
|
|
||||||
enter-to-class=""
|
|
||||||
leave-class="opacity-100"
|
|
||||||
leave-active-class="transition duration-100 ease-in"
|
|
||||||
leave-to-class="opacity-0"
|
|
||||||
>
|
|
||||||
<div v-show="open" ref="tooltip" class="z-10 overflow-hidden bg-white rounded-md shadow-lg dark:bg-gray-800 ring-1 u-ring-gray-200" :class="dropdownClass">
|
|
||||||
<div v-if="searchable" class="w-full border-b u-border-gray-200">
|
|
||||||
<TwInput
|
|
||||||
ref="search"
|
|
||||||
v-model="q"
|
|
||||||
type="search"
|
|
||||||
:name="`select-search-${name}`"
|
|
||||||
block
|
|
||||||
autocomplete="off"
|
|
||||||
appearance="none"
|
|
||||||
:placeholder="placeholderSearch"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<ul
|
|
||||||
ref="options"
|
|
||||||
tabindex="-1"
|
|
||||||
role="listbox"
|
|
||||||
class="overflow-y-auto max-h-60 sm:text-sm focus:outline-none"
|
|
||||||
>
|
>
|
||||||
<li
|
<li :class="resolveOptionClass({ active, disabled })">
|
||||||
v-if="showNewOption"
|
<span :class="[selected ? 'font-semibold' : 'font-normal', 'block truncate']">
|
||||||
ref="option-new"
|
<slot name="option" :option="option">
|
||||||
role="option"
|
{{ option[textAttribute] }}
|
||||||
class="relative pl-3 pr-12 cursor-default select-none group hover:text-white hover:bg-primary-600"
|
</slot>
|
||||||
:class="{
|
</span>
|
||||||
'bg-primary-600 text-white': active === -1,
|
|
||||||
'u-text-gray-900': active !== -1,
|
<span v-if="selected" :class="resolveOptionIconClass({ active })">
|
||||||
'py-2': dropdownSize === 'md',
|
<Icon name="heroicons-solid:check" :class="listOptionIconSizeClass" aria-hidden="true" />
|
||||||
'py-1 text-sm': dropdownSize === 'sm'
|
|
||||||
}"
|
|
||||||
@mouseover="active = -1"
|
|
||||||
@click="active === -1 && newOption()"
|
|
||||||
>
|
|
||||||
<slot name="newOption" :optionName="q">
|
|
||||||
<span class="block truncate">Add new option: "{{ q }}"</span>
|
|
||||||
</slot>
|
|
||||||
</li>
|
|
||||||
<li
|
|
||||||
v-for="(option, index) in filteredNormalizedOptions"
|
|
||||||
:key="index"
|
|
||||||
:ref="`option-${index}`"
|
|
||||||
role="option"
|
|
||||||
class="relative pl-3 pr-12 cursor-default select-none group hover:text-white hover:bg-primary-600"
|
|
||||||
:class="{
|
|
||||||
'font-semibold': isOptionSelected(option),
|
|
||||||
'bg-primary-600 text-white': active === index,
|
|
||||||
'u-text-gray-900': active !== index,
|
|
||||||
'py-2': dropdownSize === 'md',
|
|
||||||
'py-1 text-sm': dropdownSize === 'sm'
|
|
||||||
}"
|
|
||||||
@mouseover="active = index"
|
|
||||||
@click.prevent="active === index && selectOption(option)"
|
|
||||||
>
|
|
||||||
<slot name="option" :option="option">
|
|
||||||
<span class="block truncate">{{ option[textAttribute] }}</span>
|
|
||||||
</slot>
|
|
||||||
<span class="absolute inset-y-0 right-0 flex items-center pr-3">
|
|
||||||
<Icon
|
|
||||||
v-if="isOptionSelected(option)"
|
|
||||||
name="solid/check"
|
|
||||||
class=" group-hover:text-white"
|
|
||||||
:class="{
|
|
||||||
'text-white': active === index,
|
|
||||||
'text-primary-600': active !== index,
|
|
||||||
'h-5 w-5': dropdownSize === 'md',
|
|
||||||
'h-4 w-4': dropdownSize === 'sm'
|
|
||||||
}"
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ListboxOption>
|
||||||
</div>
|
</ListboxOptions>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</Listbox>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import { get } from 'lodash-es'
|
import { computed } from 'vue'
|
||||||
import { createPopper } from '@popperjs/core'
|
import {
|
||||||
// import { directive as onClickaway } from 'vue-clickaway'
|
Listbox,
|
||||||
|
ListboxButton,
|
||||||
|
ListboxOptions,
|
||||||
|
ListboxOption
|
||||||
|
} from '@headlessui/vue'
|
||||||
import Icon from '../elements/Icon'
|
import Icon from '../elements/Icon'
|
||||||
|
import { classNames } from '../../utils'
|
||||||
|
import $ui from '#build/ui'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
components: {
|
modelValue: {
|
||||||
Icon
|
type: [String, Number, Object],
|
||||||
|
default: ''
|
||||||
},
|
},
|
||||||
// directives: {
|
options: {
|
||||||
// onClickaway
|
type: Array,
|
||||||
// },
|
default: () => []
|
||||||
shortcuts: {
|
},
|
||||||
disabled () {
|
size: {
|
||||||
return !this.open
|
type: String,
|
||||||
},
|
default: 'md',
|
||||||
up: 'prev',
|
validator (value) {
|
||||||
down: 'next',
|
return Object.keys($ui.selectCustom.size).includes(value)
|
||||||
enter: 'enter',
|
|
||||||
esc: {
|
|
||||||
handler: 'close',
|
|
||||||
stop: true,
|
|
||||||
prevent: true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
props: {
|
wrapperClass: {
|
||||||
value: {
|
type: String,
|
||||||
type: [String, Number, Object, Array],
|
default: () => $ui.selectCustom.wrapper
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
multiple: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
type: Array,
|
|
||||||
default: () => []
|
|
||||||
},
|
|
||||||
textAttribute: {
|
|
||||||
type: String,
|
|
||||||
default: 'text'
|
|
||||||
},
|
|
||||||
valueAttribute: {
|
|
||||||
type: String,
|
|
||||||
default: 'value'
|
|
||||||
},
|
|
||||||
searchAttributes: {
|
|
||||||
type: Array,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
disabled: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
placeholder: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
placeholderSearch: {
|
|
||||||
type: String,
|
|
||||||
default: 'Search...'
|
|
||||||
},
|
|
||||||
searchable: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
required: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
newEnabled: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
type: String,
|
|
||||||
default: 'md',
|
|
||||||
validator (value) {
|
|
||||||
return ['xxs', 'xs', 'sm', 'md', 'lg', 'xl'].includes(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dropdownClass: {
|
|
||||||
type: String,
|
|
||||||
default: 'w-full'
|
|
||||||
},
|
|
||||||
dropdownSize: {
|
|
||||||
type: String,
|
|
||||||
default: 'md',
|
|
||||||
validator (value) {
|
|
||||||
return ['sm', 'md'].includes(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
variant: {
|
|
||||||
type: String,
|
|
||||||
default: 'gray'
|
|
||||||
},
|
|
||||||
strategy: {
|
|
||||||
type: String,
|
|
||||||
default: 'absolute'
|
|
||||||
},
|
|
||||||
placement: {
|
|
||||||
type: String,
|
|
||||||
default: 'bottom-start'
|
|
||||||
},
|
|
||||||
unselectable: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
data () {
|
baseClass: {
|
||||||
return {
|
type: String,
|
||||||
open: false,
|
default: () => $ui.selectCustom.base
|
||||||
active: 0,
|
|
||||||
q: '',
|
|
||||||
instance: null
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
computed: {
|
iconBaseClass: {
|
||||||
showNewOption () {
|
type: String,
|
||||||
return this.newEnabled && this.q && !this.filteredNormalizedOptions.find(option => option[this.textAttribute].toLowerCase() === this.q.toLowerCase())
|
default: () => $ui.selectCustom.icon.base
|
||||||
},
|
|
||||||
selectedOptions () {
|
|
||||||
if (this.multiple) {
|
|
||||||
return this.value.map(value => this.normalizedOptions.find(option => option[this.valueAttribute] === value)).filter(Boolean)
|
|
||||||
} else {
|
|
||||||
return [this.normalizedOptions.find(option => option[this.valueAttribute] === this.value)].filter(Boolean)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
normalizedOptions () {
|
|
||||||
return this.options.map(option => this.normalizeOption(option))
|
|
||||||
},
|
|
||||||
filteredNormalizedOptions () {
|
|
||||||
let filteredNormalizedOptions = this.normalizedOptions
|
|
||||||
|
|
||||||
if (!this.q) {
|
|
||||||
return filteredNormalizedOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
filteredNormalizedOptions = this.normalizedOptions.filter((option) => {
|
|
||||||
return (this.searchAttributes?.length ? this.searchAttributes : [this.textAttribute]).some((searchAttribute) => {
|
|
||||||
return option[searchAttribute] && option[searchAttribute].search(new RegExp(this.q, 'i')) !== -1
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
return filteredNormalizedOptions
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
watch: {
|
customClass: {
|
||||||
disabled (value) {
|
type: String,
|
||||||
if (value && open) { this.close() }
|
default: null
|
||||||
},
|
|
||||||
open (value) {
|
|
||||||
this.$emit('open', value)
|
|
||||||
|
|
||||||
if (!value) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.searchable) {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.$refs.search.$refs.input.focus()
|
|
||||||
this.$refs.search.$refs.input.select()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.multiple) {
|
|
||||||
if (this.value.length) {
|
|
||||||
this.active = this.filteredNormalizedOptions.findIndex(option => this.value.includes(option[this.valueAttribute]))
|
|
||||||
}
|
|
||||||
} else if (this.value) {
|
|
||||||
this.active = this.filteredNormalizedOptions.findIndex(option => option[this.valueAttribute] === this.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.instance) {
|
|
||||||
this.instance.destroy()
|
|
||||||
this.instance = null
|
|
||||||
}
|
|
||||||
|
|
||||||
this.instance = createPopper(this.$refs.container, this.$refs.tooltip, {
|
|
||||||
strategy: this.strategy,
|
|
||||||
placement: this.placement,
|
|
||||||
modifiers: [
|
|
||||||
{
|
|
||||||
name: 'offset',
|
|
||||||
options: {
|
|
||||||
offset: [0, 8]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'computeStyles',
|
|
||||||
options: {
|
|
||||||
gpuAcceleration: false,
|
|
||||||
adaptive: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'preventOverflow',
|
|
||||||
options: {
|
|
||||||
padding: 8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.scrollIntoView()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
filteredNormalizedOptions () {
|
|
||||||
this.updateActive()
|
|
||||||
},
|
|
||||||
q () {
|
|
||||||
this.updateActive()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
listBaseClass: {
|
||||||
if (this.instance) {
|
type: String,
|
||||||
this.instance.destroy()
|
default: () => $ui.selectCustom.list.base
|
||||||
this.instance = null
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
methods: {
|
listOptionBaseClass: {
|
||||||
toggle () {
|
type: String,
|
||||||
this.open = !this.open
|
default: () => $ui.selectCustom.list.option.base
|
||||||
},
|
},
|
||||||
close () {
|
listOptionActiveClass: {
|
||||||
this.open = false
|
type: String,
|
||||||
},
|
default: () => $ui.selectCustom.list.option.active
|
||||||
newOption () {
|
},
|
||||||
this.$emit('new', this.q)
|
listOptionInactiveClass: {
|
||||||
},
|
type: String,
|
||||||
isOptionSelected (option) {
|
default: () => $ui.selectCustom.list.option.inactive
|
||||||
if (this.multiple) {
|
},
|
||||||
return this.value && this.value.find(it => it === option[this.valueAttribute])
|
listOptionDisabledClass: {
|
||||||
}
|
type: String,
|
||||||
|
default: () => $ui.selectCustom.list.option.disabled
|
||||||
return this.value && this.value === option[this.valueAttribute]
|
},
|
||||||
},
|
listOptionIconBaseClass: {
|
||||||
selectOption (option) {
|
type: String,
|
||||||
if (this.multiple) {
|
default: () => $ui.selectCustom.list.option.icon.base
|
||||||
const value = [...this.value]
|
},
|
||||||
const index = value.findIndex(it => it === option[this.valueAttribute])
|
listOptionIconActiveClass: {
|
||||||
if (index > -1) {
|
type: String,
|
||||||
value.splice(index, 1)
|
default: () => $ui.selectCustom.list.option.icon.active
|
||||||
} else {
|
},
|
||||||
value.push(option[this.valueAttribute])
|
listOptionIconInactiveClass: {
|
||||||
}
|
type: String,
|
||||||
this.$emit('input', value)
|
default: () => $ui.selectCustom.list.option.icon.inactive
|
||||||
} else {
|
},
|
||||||
if (this.isOptionSelected(option)) {
|
listOptionIconSizeClass: {
|
||||||
if (this.unselectable) {
|
type: String,
|
||||||
this.$emit('input', null)
|
default: () => $ui.selectCustom.list.option.icon.size
|
||||||
}
|
},
|
||||||
} else {
|
textAttribute: {
|
||||||
this.$emit('input', option[this.valueAttribute])
|
type: String,
|
||||||
}
|
default: 'text'
|
||||||
this.open = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
guessOptionValue (option) {
|
|
||||||
return get(option, this.valueAttribute, get(option, this.textAttribute))
|
|
||||||
},
|
|
||||||
guessOptionText (option) {
|
|
||||||
return get(option, this.textAttribute, get(option, this.valueAttribute))
|
|
||||||
},
|
|
||||||
normalizeOption (option) {
|
|
||||||
if (['string', 'number', 'boolean'].includes(typeof option)) {
|
|
||||||
return {
|
|
||||||
[this.valueAttribute]: option,
|
|
||||||
[this.textAttribute]: option
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...option,
|
|
||||||
[this.valueAttribute]: this.guessOptionValue(option),
|
|
||||||
[this.textAttribute]: this.guessOptionText(option)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
prev () {
|
|
||||||
if (this.active - 1 >= (this.showNewOption ? -1 : 0)) {
|
|
||||||
this.active--
|
|
||||||
}
|
|
||||||
|
|
||||||
this.scrollIntoView()
|
|
||||||
},
|
|
||||||
next () {
|
|
||||||
if (this.active + 1 <= (this.filteredNormalizedOptions.length - 1)) {
|
|
||||||
this.active++
|
|
||||||
}
|
|
||||||
|
|
||||||
this.scrollIntoView()
|
|
||||||
},
|
|
||||||
enter () {
|
|
||||||
if (this.active === -1) {
|
|
||||||
if (this.showNewOption) {
|
|
||||||
this.newOption()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const option = this.filteredNormalizedOptions[this.active]
|
|
||||||
if (!option) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.selectOption(option)
|
|
||||||
},
|
|
||||||
scrollIntoView () {
|
|
||||||
let child
|
|
||||||
if (this.active === -1) {
|
|
||||||
child = this.$refs['option-new']
|
|
||||||
} else {
|
|
||||||
child = this.$refs[`option-${this.active}`][0]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!child) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
child.scrollIntoView({ block: 'nearest' })
|
|
||||||
},
|
|
||||||
updateActive () {
|
|
||||||
this.active = this.showNewOption && !this.filteredNormalizedOptions.length ? -1 : 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
|
const selectCustomClass = computed(() => {
|
||||||
|
return classNames(
|
||||||
|
props.baseClass,
|
||||||
|
$ui.selectCustom.size[props.size],
|
||||||
|
$ui.selectCustom.spacing[props.size],
|
||||||
|
$ui.selectCustom.appearance.default,
|
||||||
|
$ui.selectCustom.trailing.spacing[props.size],
|
||||||
|
props.customClass
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const iconClass = computed(() => {
|
||||||
|
return classNames(
|
||||||
|
props.iconBaseClass,
|
||||||
|
$ui.selectCustom.icon.size[props.size],
|
||||||
|
$ui.selectCustom.icon.trailing.spacing[props.size]
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const iconWrapperClass = $ui.selectCustom.icon.trailing.wrapper
|
||||||
|
|
||||||
|
function resolveOptionClass ({ active, disabled }) {
|
||||||
|
return classNames(
|
||||||
|
props.listOptionBaseClass,
|
||||||
|
active ? props.listOptionActiveClass : props.listOptionInactiveClass,
|
||||||
|
disabled && props.listOptionDisabledClass
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveOptionIconClass ({ active }) {
|
||||||
|
return classNames(
|
||||||
|
props.listOptionIconBaseClass,
|
||||||
|
active ? props.listOptionIconActiveClass : props.listOptionIconInactiveClass
|
||||||
|
)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,148 +0,0 @@
|
|||||||
[multiple],
|
|
||||||
[type=date],
|
|
||||||
[type=datetime-local],
|
|
||||||
[type=email],
|
|
||||||
[type=month],
|
|
||||||
[type=number],
|
|
||||||
[type=password],
|
|
||||||
[type=search],
|
|
||||||
[type=tel],
|
|
||||||
[type=text],
|
|
||||||
[type=time],
|
|
||||||
[type=url],
|
|
||||||
[type=week],
|
|
||||||
select,
|
|
||||||
textarea {
|
|
||||||
-webkit-appearance: none;
|
|
||||||
-moz-appearance: none;
|
|
||||||
appearance: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
input::-moz-placeholder,
|
|
||||||
textarea::-moz-placeholder {
|
|
||||||
color: #6b7280;
|
|
||||||
opacity: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
input:-ms-input-placeholder,
|
|
||||||
textarea:-ms-input-placeholder {
|
|
||||||
color: #6b7280;
|
|
||||||
opacity: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
input::placeholder,
|
|
||||||
textarea::placeholder {
|
|
||||||
color: #6b7280;
|
|
||||||
opacity: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-datetime-edit-fields-wrapper {
|
|
||||||
padding: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-date-and-time-value {
|
|
||||||
min-height: 1.5em
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
|
|
||||||
background-position: right .5rem center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: 1.5em 1.5em;
|
|
||||||
padding-right: 2.5rem;
|
|
||||||
-webkit-print-color-adjust: exact;
|
|
||||||
color-adjust: exact
|
|
||||||
}
|
|
||||||
|
|
||||||
[multiple] {
|
|
||||||
background-image: initial;
|
|
||||||
background-position: initial;
|
|
||||||
background-repeat: unset;
|
|
||||||
background-size: initial;
|
|
||||||
padding-right: .75rem;
|
|
||||||
-webkit-print-color-adjust: unset;
|
|
||||||
color-adjust: unset
|
|
||||||
}
|
|
||||||
|
|
||||||
[type=checkbox],
|
|
||||||
[type=radio] {
|
|
||||||
-webkit-appearance: none;
|
|
||||||
-moz-appearance: none;
|
|
||||||
appearance: none;
|
|
||||||
padding: 0;
|
|
||||||
-webkit-print-color-adjust: exact;
|
|
||||||
color-adjust: exact;
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
background-origin: border-box;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
flex-shrink: 0;
|
|
||||||
height: 1rem;
|
|
||||||
width: 1rem;
|
|
||||||
border-width: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
[type=checkbox] {
|
|
||||||
border-radius: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
[type=radio] {
|
|
||||||
border-radius: 100%
|
|
||||||
}
|
|
||||||
|
|
||||||
[type=checkbox]:checked,
|
|
||||||
[type=radio]:checked {
|
|
||||||
border-color: transparent;
|
|
||||||
background-color: currentColor;
|
|
||||||
background-size: 100% 100%;
|
|
||||||
background-position: center;
|
|
||||||
background-repeat: no-repeat
|
|
||||||
}
|
|
||||||
|
|
||||||
[type=checkbox]:checked {
|
|
||||||
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e")
|
|
||||||
}
|
|
||||||
|
|
||||||
[type=radio]:checked {
|
|
||||||
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e")
|
|
||||||
}
|
|
||||||
|
|
||||||
[type=checkbox]:checked:focus,
|
|
||||||
[type=checkbox]:checked:hover,
|
|
||||||
[type=radio]:checked:focus,
|
|
||||||
[type=radio]:checked:hover {
|
|
||||||
border-color: transparent;
|
|
||||||
background-color: currentColor
|
|
||||||
}
|
|
||||||
|
|
||||||
[type=checkbox]:indeterminate {
|
|
||||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");
|
|
||||||
border-color: transparent;
|
|
||||||
background-color: currentColor;
|
|
||||||
background-size: 100% 100%;
|
|
||||||
background-position: center;
|
|
||||||
background-repeat: no-repeat
|
|
||||||
}
|
|
||||||
|
|
||||||
[type=checkbox]:indeterminate:focus,
|
|
||||||
[type=checkbox]:indeterminate:hover {
|
|
||||||
border-color: transparent;
|
|
||||||
background-color: currentColor
|
|
||||||
}
|
|
||||||
|
|
||||||
[type=file] {
|
|
||||||
background: unset;
|
|
||||||
border-color: inherit;
|
|
||||||
border-width: 0;
|
|
||||||
border-radius: 0;
|
|
||||||
padding: 0;
|
|
||||||
font-size: unset;
|
|
||||||
line-height: inherit
|
|
||||||
}
|
|
||||||
|
|
||||||
[type=file]:focus {
|
|
||||||
outline: 1px auto -webkit-focus-ring-color
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { defineNuxtPlugin } from '#app'
|
import { defineNuxtPlugin } from '#app'
|
||||||
import { ClipboardPlugin } from '../types/clipboard'
|
import { ClipboardPlugin } from '../types'
|
||||||
|
|
||||||
export default defineNuxtPlugin((nuxtApp) => {
|
export default defineNuxtPlugin((nuxtApp) => {
|
||||||
function copy (text: string, success: { title?: string, description?: string } = {}, failure: { title?: string, description?: string } = {}) {
|
function copy (text: string, success: { title?: string, description?: string } = {}, failure: { title?: string, description?: string } = {}) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { nanoid } from 'nanoid'
|
import { nanoid } from 'nanoid'
|
||||||
import { Ref } from 'vue'
|
import { Ref } from 'vue'
|
||||||
import { defineNuxtPlugin, useState } from '#app'
|
import { defineNuxtPlugin, useState } from '#app'
|
||||||
import { ToastNotification, ToastPlugin } from '../types/toast'
|
import { ToastNotification, ToastPlugin } from '../types'
|
||||||
|
|
||||||
export default defineNuxtPlugin((nuxtApp) => {
|
export default defineNuxtPlugin((nuxtApp) => {
|
||||||
const notifications: Ref<ToastNotification[]> = useState('notifications', () => [])
|
const notifications: Ref<ToastNotification[]> = useState('notifications', () => [])
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ const formGroup = {
|
|||||||
|
|
||||||
const input = {
|
const input = {
|
||||||
wrapper: 'relative',
|
wrapper: 'relative',
|
||||||
base: 'block w-full u-bg-white u-text-gray-700 disabled:cursor-not-allowed disabled:opacity-75 focus:outline-none',
|
base: 'relative block w-full u-bg-white u-text-gray-700 disabled:cursor-not-allowed disabled:opacity-75 focus:outline-none',
|
||||||
size: {
|
size: {
|
||||||
xxs: 'text-xs',
|
xxs: 'text-xs',
|
||||||
xs: 'text-xs',
|
xs: 'text-xs',
|
||||||
@@ -204,6 +204,26 @@ const select = {
|
|||||||
...input
|
...input
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const selectCustom = {
|
||||||
|
...select,
|
||||||
|
base: `${select.base} text-left cursor-default`,
|
||||||
|
list: {
|
||||||
|
base: 'absolute z-10 mt-1 w-full bg-white shadow-lg max-h-60 rounded-md py-1 text-base ring-1 u-ring-gray-200 overflow-auto focus:outline-none sm:text-sm',
|
||||||
|
option: {
|
||||||
|
base: 'cursor-default select-none relative py-2 pl-4 pr-10',
|
||||||
|
active: 'text-white bg-primary-600',
|
||||||
|
inactive: 'u-text-gray-900',
|
||||||
|
disabled: 'cursor-not-allowed opacity-50',
|
||||||
|
icon: {
|
||||||
|
base: 'absolute inset-y-0 right-0 flex items-center pr-4',
|
||||||
|
active: 'text-white',
|
||||||
|
inactive: 'text-primary-600',
|
||||||
|
size: 'h-5 w-5'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const radio = {
|
const radio = {
|
||||||
wrapper: 'relative flex items-start',
|
wrapper: 'relative flex items-start',
|
||||||
base: 'h-4 w-4 text-primary-600 focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 focus:ring-offset-white dark:focus:ring-offset-black u-border-gray-300 dark:checked:border-primary-600 disabled:opacity-50 disabled:cursor-not-allowed',
|
base: 'h-4 w-4 text-primary-600 focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 focus:ring-offset-white dark:focus:ring-offset-black u-border-gray-300 dark:checked:border-primary-600 disabled:opacity-50 disabled:cursor-not-allowed',
|
||||||
@@ -281,7 +301,7 @@ const alertDialog = {
|
|||||||
const dropdown = {
|
const dropdown = {
|
||||||
wrapper: 'relative inline-flex text-left',
|
wrapper: 'relative inline-flex text-left',
|
||||||
container: 'w-48 z-20',
|
container: 'w-48 z-20',
|
||||||
base: 'u-bg-white divide-y u-divide-gray-100 rounded-md ring-1 ring-black ring-opacity-5',
|
base: 'u-bg-white divide-y u-divide-gray-100 rounded-md ring-1 u-ring-gray-200 shadow-lg',
|
||||||
item: {
|
item: {
|
||||||
base: 'group flex items-center px-4 py-2 text-sm w-full',
|
base: 'group flex items-center px-4 py-2 text-sm w-full',
|
||||||
active: 'u-bg-gray-100 u-text-gray-900',
|
active: 'u-bg-gray-100 u-text-gray-900',
|
||||||
@@ -299,6 +319,7 @@ export default {
|
|||||||
input,
|
input,
|
||||||
textarea,
|
textarea,
|
||||||
select,
|
select,
|
||||||
|
selectCustom,
|
||||||
checkbox,
|
checkbox,
|
||||||
radio,
|
radio,
|
||||||
container,
|
container,
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ const select = {
|
|||||||
...input
|
...input
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const selectCustom = {
|
||||||
|
...input
|
||||||
|
}
|
||||||
|
|
||||||
const radio = {
|
const radio = {
|
||||||
base: 'h-4 w-4 u-text-gray-900 focus:ring-2 focus:ring-offset-2 focus:u-ring-gray-900 focus:ring-offset-white dark:focus:ring-offset-black u-border-gray-300 disabled:opacity-50 disabled:cursor-not-allowed'
|
base: 'h-4 w-4 u-text-gray-900 focus:ring-2 focus:ring-offset-2 focus:u-ring-gray-900 focus:ring-offset-white dark:focus:ring-offset-black u-border-gray-300 disabled:opacity-50 disabled:cursor-not-allowed'
|
||||||
}
|
}
|
||||||
@@ -53,6 +57,7 @@ export default defu({
|
|||||||
input,
|
input,
|
||||||
textarea,
|
textarea,
|
||||||
select,
|
select,
|
||||||
|
selectCustom,
|
||||||
radio,
|
radio,
|
||||||
checkbox,
|
checkbox,
|
||||||
toggle,
|
toggle,
|
||||||
|
|||||||
63
src/runtime/tailwind.css
Normal file
63
src/runtime/tailwind.css
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@layer utilities {
|
||||||
|
.u-bg-white { @apply bg-white dark:bg-black; }
|
||||||
|
.u-bg-gray-50 { @apply bg-gray-50 dark:bg-gray-900; }
|
||||||
|
.u-bg-gray-100 { @apply bg-gray-100 dark:bg-gray-800; }
|
||||||
|
.u-bg-gray-200 { @apply bg-gray-200 dark:bg-gray-700; }
|
||||||
|
.u-bg-gray-300 { @apply bg-gray-300 dark:bg-gray-600; }
|
||||||
|
.u-bg-gray-400 { @apply bg-gray-400 dark:bg-gray-500; }
|
||||||
|
.u-bg-gray-500 { @apply bg-gray-500 dark:bg-gray-400; }
|
||||||
|
.u-bg-gray-600 { @apply bg-gray-600 dark:bg-gray-300; }
|
||||||
|
.u-bg-gray-700 { @apply bg-gray-700 dark:bg-gray-200; }
|
||||||
|
.u-bg-gray-800 { @apply bg-gray-800 dark:bg-gray-100; }
|
||||||
|
.u-bg-gray-900 { @apply bg-gray-900 dark:bg-gray-50; }
|
||||||
|
.u-bg-black { @apply bg-black dark:bg-white; }
|
||||||
|
.u-text-white { @apply text-white dark:text-black; }
|
||||||
|
.u-text-gray-50 { @apply text-gray-50 dark:text-gray-900; }
|
||||||
|
.u-text-gray-100 { @apply text-gray-100 dark:text-gray-800; }
|
||||||
|
.u-text-gray-200 { @apply text-gray-200 dark:text-gray-700; }
|
||||||
|
.u-text-gray-300 { @apply text-gray-300 dark:text-gray-600; }
|
||||||
|
.u-text-gray-400 { @apply text-gray-400 dark:text-gray-500; }
|
||||||
|
.u-text-gray-500 { @apply text-gray-500 dark:text-gray-400; }
|
||||||
|
.u-text-gray-600 { @apply text-gray-600 dark:text-gray-300; }
|
||||||
|
.u-text-gray-700 { @apply text-gray-700 dark:text-gray-200; }
|
||||||
|
.u-text-gray-800 { @apply text-gray-800 dark:text-gray-100; }
|
||||||
|
.u-text-gray-900 { @apply text-gray-900 dark:text-gray-50; }
|
||||||
|
.u-text-black { @apply text-black dark:text-white; }
|
||||||
|
.u-border-white { @apply border-white dark:border-black; }
|
||||||
|
.u-border-gray-100 { @apply border-gray-100 dark:border-gray-900; }
|
||||||
|
.u-border-gray-200 { @apply border-gray-200 dark:border-gray-800; }
|
||||||
|
.u-border-gray-300 { @apply border-gray-300 dark:border-gray-700; }
|
||||||
|
.u-border-gray-400 { @apply border-gray-400 dark:border-gray-600; }
|
||||||
|
.u-border-gray-500 { @apply border-gray-500 dark:border-gray-500; }
|
||||||
|
.u-border-gray-600 { @apply border-gray-600 dark:border-gray-400; }
|
||||||
|
.u-border-gray-700 { @apply border-gray-700 dark:border-gray-300; }
|
||||||
|
.u-border-gray-800 { @apply border-gray-800 dark:border-gray-200; }
|
||||||
|
.u-border-gray-900 { @apply border-gray-900 dark:border-gray-100; }
|
||||||
|
.u-border-black { @apply border-black dark:border-white; }
|
||||||
|
.u-divide-white { @apply divide-white dark:divide-black; }
|
||||||
|
.u-divide-gray-100 { @apply divide-gray-100 dark:divide-gray-900; }
|
||||||
|
.u-divide-gray-200 { @apply divide-gray-200 dark:divide-gray-800; }
|
||||||
|
.u-divide-gray-300 { @apply divide-gray-300 dark:divide-gray-700; }
|
||||||
|
.u-divide-gray-400 { @apply divide-gray-400 dark:divide-gray-600; }
|
||||||
|
.u-divide-gray-500 { @apply divide-gray-500 dark:divide-gray-500; }
|
||||||
|
.u-divide-gray-600 { @apply divide-gray-600 dark:divide-gray-400; }
|
||||||
|
.u-divide-gray-700 { @apply divide-gray-700 dark:divide-gray-300; }
|
||||||
|
.u-divide-gray-800 { @apply divide-gray-800 dark:divide-gray-200; }
|
||||||
|
.u-divide-gray-900 { @apply divide-gray-900 dark:divide-gray-100; }
|
||||||
|
.u-divide-black { @apply divide-black dark:divide-white; }
|
||||||
|
.u-ring-white { @apply ring-white dark:ring-black; }
|
||||||
|
.u-ring-gray-100 { @apply ring-gray-100 dark:ring-gray-900; }
|
||||||
|
.u-ring-gray-200 { @apply ring-gray-200 dark:ring-gray-800; }
|
||||||
|
.u-ring-gray-300 { @apply ring-gray-300 dark:ring-gray-700; }
|
||||||
|
.u-ring-gray-400 { @apply ring-gray-400 dark:ring-gray-600; }
|
||||||
|
.u-ring-gray-500 { @apply ring-gray-500 dark:ring-gray-500; }
|
||||||
|
.u-ring-gray-600 { @apply ring-gray-600 dark:ring-gray-400; }
|
||||||
|
.u-ring-gray-700 { @apply ring-gray-700 dark:ring-gray-300; }
|
||||||
|
.u-ring-gray-800 { @apply ring-gray-800 dark:ring-gray-200; }
|
||||||
|
.u-ring-gray-900 { @apply ring-gray-900 dark:ring-gray-100; }
|
||||||
|
.u-ring-black { @apply ring-black dark:ring-white; }
|
||||||
|
}
|
||||||
2
src/runtime/types/index.d.ts
vendored
Normal file
2
src/runtime/types/index.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './clipboard'
|
||||||
|
export * from './toast'
|
||||||
Reference in New Issue
Block a user