feat: support presets (#14)

This commit is contained in:
Benjamin Canac
2021-11-26 14:22:43 +01:00
committed by GitHub
parent e05cc89866
commit 8f09d0c5ef
11 changed files with 351 additions and 269 deletions

View File

@@ -2,6 +2,7 @@ export default {
entries: [
'./src/index',
{ input: './src/components/', outDir: 'dist/components', ext: 'js' },
{ input: './src/presets/', outDir: 'dist/presets', ext: 'js' },
{ input: './src/utils/', outDir: 'dist/utils', ext: 'js' }
],
declaration: true,

View File

@@ -55,7 +55,7 @@
import { UseDark } from '@vueuse/components'
const sections = [
{ label: 'Getting Started', links: [{ label: 'Installation', 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: 'Link', to: '/components/Link' }, { label: 'Toggle', to: '/components/Toggle' }] },
{ label: 'Feedback', links: [{ label: 'Alert', to: '/components/Alert' }] },
{ label: 'Forms', links: [{ label: 'Checkbox', to: '/components/Checkbox' }, { label: 'Input', to: '/components/Input' }, { label: 'InputGroup', to: '/components/InputGroup' }, { label: 'Radio', to: '/components/Radio' }, { label: 'RadioGroup', to: '/components/RadioGroup' }, { label: 'Select', to: '/components/Select' }, { label: 'SelectCustom', to: '/components/SelectCustom' }, { label: 'Textarea', to: '/components/Textarea' }] },

View File

@@ -54,6 +54,8 @@
</template>
<script setup>
import $ui from '#build/ui'
const nuxtApp = useNuxtApp()
const { params } = useRoute()
@@ -74,8 +76,15 @@ const refProps = Object.entries(componentProps).map(([key, prop]) => {
let values
if (prop.validator) {
const result = prop.validator.toString().match(/\[.*\]/g, '')[0]
values = JSON.parse(result.replace(/'/g, '"')).filter(Boolean)
const arrayRegex = prop.validator.toString().match(/\[.*\]/g, '')
if (arrayRegex) {
values = JSON.parse(arrayRegex[0].replace(/'/g, '"')).filter(Boolean)
} else {
const $uiProp = $ui[params.component.toLowerCase()][key]
if ($uiProp) {
values = Object.keys($uiProp).filter(Boolean)
}
}
}
if (value) {

View File

@@ -49,6 +49,10 @@ export default defineNuxtConfig({
Options
</h2>
<p>- `preset`</p>
<p>Choose preset. Defaults to `tailwindui`. An object can also be used to override some parts of the default preset.</p>
<p>- `prefix`</p>
<p>Define the prefix of the imported components. Defaults to `u`.</p>

View File

@@ -20,7 +20,10 @@
Component
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium u-text-gray-500 uppercase tracking-wider">
Ready?
Composition API
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium u-text-gray-500 uppercase tracking-wider">
Preset system
</th>
</tr>
</thead>
@@ -32,7 +35,11 @@
</NuxtLink>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm u-text-gray-500">
<span v-if="component.ready"></span>
<span v-if="component.capi"></span>
<span v-else></span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm u-text-gray-500">
<span v-if="component.preset"></span>
<span v-else></span>
</td>
</tr>
@@ -63,12 +70,12 @@ const components = [
{
label: 'Dropdown',
to: '/components/Dropdown',
ready: true
capi: true
},
{
label: 'Icon',
to: '/components/Icon',
ready: true
capi: true
},
{
label: 'Link',
@@ -89,7 +96,8 @@ const components = [
{
label: 'Input',
to: '/components/Input',
ready: true
capi: true,
preset: true
},
{
label: 'InputGroup',
@@ -105,7 +113,9 @@ const components = [
},
{
label: 'Select',
to: '/components/Select'
to: '/components/Select',
capi: true,
preset: true
},
{
label: 'SelectCustom',
@@ -114,17 +124,18 @@ const components = [
{
label: 'Textarea',
to: '/components/Textarea',
ready: true
capi: true,
preset: true
},
{
label: 'Card',
to: '/components/Card',
ready: true
capi: true
},
{
label: 'Container',
to: '/components/Container',
ready: true
capi: true
},
{
label: 'Pills',
@@ -141,7 +152,7 @@ const components = [
{
label: 'Modal',
to: '/components/Modal',
ready: true
capi: true
},
{
label: 'Notification',
@@ -150,7 +161,7 @@ const components = [
{
label: 'Popover',
to: '/components/Popover',
ready: true
capi: true
},
{
label: 'Slideover',

View File

@@ -28,13 +28,15 @@
"@iconify-json/heroicons-solid": "^1.0.2",
"@popperjs/core": "^2.10.2",
"@unocss/nuxt": "^0.12.9",
"defu": "^5.0.0",
"gradient-avatar": "^1.0.2",
"lodash": "^4.17.21",
"pathe": "^0.2.0"
},
"devDependencies": {
"@vueuse/core": "^7.1.1",
"@vueuse/components": "^7.1.1",
"@nuxtjs/eslint-config-typescript": "7.0.2",
"@vueuse/components": "^7.1.1",
"@vueuse/core": "^7.1.1",
"eslint": "8.3.0",
"nuxt3": "3.0.0-27296423.f3082ca",
"unbuild": "0.5.13"

View File

@@ -1,14 +1,7 @@
<template>
<div :class="wrapperClass">
<div
v-if="isLeading"
class="absolute inset-y-0 left-0 flex items-center pointer-events-none"
>
<Icon
:name="iconName"
class="u-text-gray-400"
:class="iconClass"
/>
<div v-if="isLeading" :class="iconLeadingWrapperClass">
<Icon :name="iconName" :class="iconClass" />
</div>
<input
:id="name"
@@ -22,21 +15,14 @@
:readonly="readonly"
:autocomplete="autocomplete"
:spellcheck="spellcheck"
:class="[baseClass, sizeClass, paddingClass, paddingIconClass, appearanceClass, customClass]"
:class="inputClass"
@input="onInput($event.target.value)"
@focus="$emit('focus', $event)"
@blur="$emit('blur', $event)"
>
<slot />
<div
v-if="isTrailing"
class="absolute inset-y-0 right-0 flex items-center pointer-events-none"
>
<Icon
:name="iconName"
class="u-text-gray-400"
:class="iconClass"
/>
<div v-if="isTrailing" :class="iconTrailingWrapperClass">
<Icon :name="iconName" :class="iconClass" />
</div>
</div>
</template>
@@ -44,6 +30,8 @@
<script>
import { ref, computed, onMounted } from 'vue'
import Icon from '../elements/Icon'
import { classNames } from '../../utils'
import $ui from '#build/ui'
export default {
components: {
@@ -96,7 +84,7 @@ export default {
},
loadingIcon: {
type: String,
default: null
default: 'heroicons-outline:refresh'
},
trailing: {
type: Boolean,
@@ -110,16 +98,20 @@ export default {
type: String,
default: 'md',
validator (value) {
return ['', 'xxs', 'xs', 'sm', 'md', 'lg', 'xl'].includes(value)
return Object.keys($ui.input.size).includes(value)
}
},
wrapperClass: {
type: String,
default: 'relative'
default: $ui.input.wrapper
},
baseClass: {
type: String,
default: 'block w-full u-bg-white u-text-gray-700 disabled:cursor-not-allowed disabled:u-bg-gray-50 focus:outline-none'
default: () => $ui.input.base
},
iconBaseClass: {
type: String,
default: () => $ui.input.icon.base
},
customClass: {
type: String,
@@ -129,7 +121,7 @@ export default {
type: String,
default: 'default',
validator (value) {
return ['default', 'none'].includes(value)
return Object.keys($ui.input.appearance).includes(value)
}
},
loading: {
@@ -157,50 +149,6 @@ export default {
}, 100)
})
const sizeClass = computed(() => ({
xxs: 'text-xs',
xs: 'text-xs',
sm: 'text-sm leading-4',
md: 'text-sm',
lg: 'text-base',
xl: 'text-base'
})[props.size])
const paddingClass = computed(() => ({
xxs: 'px-1 py-0.5',
xs: 'px-2.5 py-1.5',
sm: 'px-3 py-2',
md: 'px-4 py-2',
lg: 'px-4 py-2',
xl: 'px-6 py-3'
})[props.size])
const appearanceClass = computed(() => ({
default: 'focus:ring-1 focus:ring-primary-500 focus:border-primary-500 border u-border-gray-300 rounded-md shadow-sm',
none: 'border-0 bg-transparent focus:ring-0 focus:shadow-none'
})[props.appearance])
const paddingIconClass = computed(() => {
return [
props.isLeading && ({
xxs: 'pl-7',
xs: 'pl-7',
sm: 'pl-10',
md: 'pl-10',
lg: 'pl-10',
xl: 'pl-10'
})[props.size],
props.isTrailing && ({
xxs: 'pr-10',
xs: 'pr-10',
sm: 'pr-10',
md: 'pr-10',
lg: 'pr-10',
xl: 'pr-10'
})[props.size]
].join(' ')
})
const isLeading = computed(() => {
return (props.icon && props.leading) || (props.icon && !props.trailing) || (props.loading && !props.trailing)
})
@@ -209,55 +157,47 @@ export default {
return (props.icon && props.trailing) || (props.loading && props.trailing)
})
const inputClass = computed(() => {
return classNames(
props.baseClass,
$ui.input.size[props.size],
$ui.input.spacing[props.size],
$ui.input.appearance[props.appearance],
isLeading.value && $ui.input.leading.spacing[props.size],
isTrailing.value && $ui.input.trailing.spacing[props.size],
props.customClass
)
})
const iconName = computed(() => {
if (props.loading) {
return props.loadingIcon || 'custom/loading'
return props.loadingIcon
}
return props.icon
})
const iconClass = computed(() => {
return [
({
xxs: 'h-3 w-3',
xs: 'h-4 w-4',
sm: 'h-5 w-5',
md: 'h-5 w-5',
lg: 'h-5 w-5',
xl: 'h-5 w-5'
})[props.size || 'sm'],
props.isLeading && ({
xxs: 'ml-2',
xs: 'ml-2',
sm: 'ml-3',
md: 'ml-3',
lg: 'ml-3',
xl: 'ml-3'
})[props.size || 'sm'],
props.isTrailing && ({
xxs: 'mr-2',
xs: 'mr-2',
sm: 'mr-3',
md: 'mr-3',
lg: 'mr-3',
xl: 'mr-3'
})[props.size || 'sm'],
({
true: 'animate-spin'
})[props.loading]
]
return classNames(
props.iconBaseClass,
$ui.input.icon.size[props.size],
isLeading.value && $ui.input.icon.leading.spacing[props.size],
isTrailing.value && $ui.input.icon.trailing.spacing[props.size],
props.loading && 'animate-spin'
)
})
const iconLeadingWrapperClass = $ui.input.icon.leading.wrapper
const iconTrailingWrapperClass = $ui.input.icon.trailing.wrapper
return {
input,
onInput,
sizeClass,
paddingClass,
paddingIconClass,
appearanceClass,
iconClass,
inputClass,
iconName,
iconClass,
iconLeadingWrapperClass,
iconTrailingWrapperClass,
isLeading,
isTrailing
}

View File

@@ -1,13 +1,16 @@
<template>
<div :class="wrapperClass">
<div v-if="icon" :class="iconWrapperClass">
<Icon :name="icon" :class="iconClass" />
</div>
<select
:id="name"
:name="name"
:required="required"
:disabled="disabled"
:readonly="readonly"
:class="selectClass"
@input="updateValue($event.target.value)"
@input="onInput($event.target.value)"
>
<template v-for="(option, index) in normalizedOptionsWithPlaceholder">
<optgroup
@@ -33,16 +36,14 @@
/>
</template>
</select>
<div v-if="icon" class="absolute inset-y-0 left-0 flex items-center pointer-events-none" :class="iconPadding">
<Icon :name="icon" :class="iconClass" />
</div>
</div>
</template>
<script>
import get from 'lodash/get'
import Icon from '../elements/Icon'
import { classNames } from '../../utils'
import $ui from '#build/ui'
export default {
components: {
@@ -73,24 +74,24 @@ export default {
type: Array,
default: () => []
},
readonly: {
type: Boolean,
default: false
},
size: {
type: String,
default: 'md',
validator (value) {
return ['xxs', 'xs', 'sm', 'md', 'lg', 'xl'].includes(value)
return Object.keys($ui.select.size).includes(value)
}
},
wrapperClass: {
type: String,
default: 'relative'
default: $ui.select.wrapper
},
baseClass: {
type: String,
default: 'block w-full disabled:cursor-not-allowed u-bg-white u-text-gray-700 disabled:u-bg-gray-50 focus:ring-1 focus:ring-primary-500 focus:border-primary-500 dark:focus:border-primary-500 border u-border-gray-300 rounded-md shadow-sm focus:outline-none'
default: $ui.select.base
},
iconBaseClass: {
type: String,
default: $ui.select.icon.base
},
customClass: {
type: String,
@@ -110,101 +111,97 @@ export default {
}
},
emits: ['update:modelValue'],
computed: {
sizeClass () {
return {
xxs: 'text-xs',
xs: 'text-xs',
sm: 'text-sm leading-4',
md: 'text-sm',
lg: 'text-base',
xl: 'text-base'
}[this.size]
},
paddingClass () {
return ({
xxs: `${this.icon ? 'pl-7' : 'pl-2'} pr-7 py-1.5`,
xs: `${this.icon ? 'pl-8' : 'pl-3'} pr-9 py-1.5`,
sm: `${this.icon ? 'pl-8' : 'pl-3'} pr-9 py-2`,
md: `${this.icon ? 'pl-10' : 'pl-3'} pr-10 py-2`,
lg: `${this.icon ? 'pl-10' : 'pl-3'} pr-10 py-2`,
xl: `${this.icon ? 'pl-12' : 'pl-4'} pr-12 py-3`
})[this.size]
},
iconClass () {
return ({
xxs: 'w-3 h-3',
xs: 'w-4 h-4',
sm: 'w-4 h-4',
md: 'w-5 h-5',
lg: 'w-5 h-5',
xl: 'w-5 h-5'
})[this.size]
},
iconPadding () {
return ({
xxs: 'pl-3',
xs: 'pl-3',
sm: 'pl-3',
md: 'pl-3',
lg: 'pl-3',
xl: 'pl-4'
})[this.size]
},
selectClass () {
return [
this.baseClass,
this.customClass,
this.sizeClass,
this.paddingClass
].join(' ')
},
normalizedOptions () {
return this.options.map(option => this.normalizeOption(option))
},
normalizedOptionsWithPlaceholder () {
if (!this.placeholder) {
return this.normalizedOptions
}
const { normalizedOptions } = this
normalizedOptions.unshift({
[this.valueAttribute]: null,
[this.textAttribute]: this.placeholder
})
return normalizedOptions
},
normalizedValue () {
const foundOption = this.normalizedOptionsWithPlaceholder.find(option => option.value === this.modelValue)
if (!foundOption) {
return null
}
setup (props, { emit }) {
const select = ref(null)
return foundOption.value
const onInput = (value) => {
emit('update:modelValue', value)
}
},
methods: {
guessOptionValue (option) {
return get(option, this.valueAttribute, get(option, this.textAttribute))
},
guessOptionText (option) {
return get(option, this.textAttribute, get(option, this.valueAttribute))
},
normalizeOption (option) {
const guessOptionValue = (option) => {
return get(option, props.valueAttribute, get(option, props.textAttribute))
}
const guessOptionText = (option) => {
return get(option, props.textAttribute, get(option, props.valueAttribute))
}
const normalizeOption = (option) => {
if (['string', 'number', 'boolean'].includes(typeof option)) {
return {
[this.valueAttribute]: option,
[this.textAttribute]: option
[props.valueAttribute]: option,
[props.textAttribute]: option
}
}
return {
...option,
[this.valueAttribute]: this.guessOptionValue(option),
[this.textAttribute]: this.guessOptionText(option)
[props.valueAttribute]: guessOptionValue(option),
[props.textAttribute]: guessOptionText(option)
}
},
updateValue (value) {
this.$emit('update:modelValue', value)
}
const normalizedOptions = computed(() => {
return props.options.map(option => normalizeOption(option))
})
const normalizedOptionsWithPlaceholder = computed(() => {
if (!props.placeholder) {
return normalizedOptions.value
}
return [
{
[props.valueAttribute]: null,
[props.textAttribute]: props.placeholder
},
...normalizedOptions.value
]
})
const normalizedValue = computed(() => {
const foundOption = normalizedOptionsWithPlaceholder.value.find(option => option.value === props.modelValue)
if (!foundOption) {
return null
}
return foundOption.value
})
const selectClass = computed(() => {
return classNames(
props.baseClass,
$ui.select.size[props.size],
$ui.select.spacing[props.size],
$ui.select.appearance.default,
!!props.icon && $ui.select.leading.spacing[props.size],
$ui.select.trailing.spacing[props.size],
props.customClass
)
})
const iconClass = computed(() => {
return classNames(
props.iconBaseClass,
$ui.select.icon.size[props.size],
!!props.icon && $ui.select.icon.leading.spacing[props.size]
)
})
const iconWrapperClass = $ui.select.icon.leading.base
return {
select,
onInput,
guessOptionValue,
guessOptionText,
normalizeOption,
normalizedOptions,
normalizedOptionsWithPlaceholder,
normalizedValue,
selectClass,
iconClass,
iconWrapperClass
}
}
}

View File

@@ -10,7 +10,7 @@
:disabled="disabled"
:placeholder="placeholder"
:autocomplete="autocomplete"
:class="[baseClass, customClass, sizeClass, paddingClass, appearanceClass, resizeClass]"
:class="textareaClass"
@input="onInput($event.target.value)"
@focus="$emit('focus', $event)"
@blur="$emit('blur', $event)"
@@ -20,6 +20,8 @@
<script>
import { ref, computed, onMounted } from 'vue'
import { classNames } from '../../utils'
import $ui from '#build/ui'
export default {
props: {
@@ -63,7 +65,7 @@ export default {
type: String,
default: 'default',
validator (value) {
return ['default', 'none'].includes(value)
return Object.keys($ui.textarea.appearance).includes(value)
}
},
resize: {
@@ -74,16 +76,16 @@ export default {
type: String,
default: 'md',
validator (value) {
return ['', 'xxs', 'xs', 'sm', 'md', 'lg', 'xl'].includes(value)
return Object.keys($ui.textarea.size).includes(value)
}
},
wrapperClass: {
type: String,
default: 'relative'
default: $ui.textarea.wrapper
},
baseClass: {
type: String,
default: 'block w-full u-bg-white u-text-gray-700 disabled:cursor-not-allowed disabled:u-bg-gray-50 focus:outline-none'
default: $ui.textarea.base
},
customClass: {
type: String,
@@ -127,40 +129,21 @@ export default {
}, 100)
})
const sizeClass = computed(() => ({
xxs: 'text-xs',
xs: 'text-xs',
sm: 'text-sm leading-4',
md: 'text-sm',
lg: 'text-base',
xl: 'text-base'
})[props.size])
const paddingClass = computed(() => ({
xxs: 'px-1 py-0.5',
xs: 'px-2.5 py-1.5',
sm: 'px-3 py-2',
md: 'px-4 py-2',
lg: 'px-4 py-2',
xl: 'px-6 py-3'
})[props.size])
const appearanceClass = computed(() => ({
default: 'focus:ring-1 focus:ring-primary-500 focus:border-primary-500 border u-border-gray-300 rounded-md shadow-sm',
none: 'border-0 bg-transparent focus:ring-0 focus:shadow-none'
})[props.appearance])
const resizeClass = computed(() => {
return props.resize ? '' : 'resize-none'
const textareaClass = computed(() => {
return classNames(
props.baseClass,
$ui.textarea.size[props.size],
$ui.textarea.spacing[props.size],
$ui.textarea.appearance[props.appearance],
!props.resize && 'resize-none',
props.customClass
)
})
return {
textarea,
onInput,
sizeClass,
paddingClass,
appearanceClass,
resizeClass
textareaClass
}
}
}

View File

@@ -1,6 +1,7 @@
import { resolve } from 'pathe'
import { defineNuxtModule, installModule, addComponentsDir } from '@nuxt/kit'
import { defineNuxtModule, installModule, addComponentsDir, addTemplate } from '@nuxt/kit'
import { colors } from '@unocss/preset-uno'
import defu from 'defu'
import type { UnocssNuxtOptions } from '@unocss/nuxt'
export interface UiColorsOptions {
@@ -17,8 +18,11 @@ export interface UiColorsOptions {
export interface UiOptions {
/**
* Prefix of injected components.
*
* @default 'tailwindui'
*/
preset?: string | object
/**
* @default 'u'
*/
prefix?: string
@@ -28,24 +32,27 @@ export interface UiOptions {
unocss?: UnocssNuxtOptions
}
const defaults = {
preset: 'tailwindui',
prefix: 'u',
colors: {
primary: 'indigo',
gray: 'zinc'
},
unocss: {
shortcuts: [],
rules: [],
variants: [],
theme: {}
}
}
export default defineNuxtModule<UiOptions>({
name: '@nuxthq/ui',
configKey: 'ui',
defaults: {
prefix: 'u',
colors: {
primary: 'indigo',
gray: 'zinc'
},
unocss: {
shortcuts: [],
rules: [],
variants: [],
theme: {}
}
},
defaults,
async setup (_options, nuxt) {
const { prefix, colors: { primary = 'indigo', gray = 'zinc' } = {} } = _options
const { preset, prefix, colors: { primary = 'indigo', gray = 'zinc' } = {} } = _options
const { shortcuts = [], rules = [], variants = [], theme = {} } = _options.unocss || {}
const options: UnocssNuxtOptions = {
@@ -134,7 +141,18 @@ export default defineNuxtModule<UiOptions>({
}],
...rules
],
variants,
variants: [
// disabled:
(matcher) => {
if (!matcher.startsWith('disabled:')) { return matcher }
return {
// slice `disabled:` prefix and passed to the next variants and rules
matcher: matcher.slice(9),
selector: s => `${s}:disabled`
}
},
...variants
],
layers: {
icons: 0,
default: 1,
@@ -144,6 +162,24 @@ export default defineNuxtModule<UiOptions>({
await installModule(nuxt, { src: '@unocss/nuxt', options })
let ui: object = {}
try {
if (typeof preset === 'object') {
ui = await import(resolve(__dirname, `./presets/${defaults.preset}`))
ui = defu(preset, ui)
} else {
ui = await import(resolve(__dirname, `./presets/${preset}`))
}
} catch (e) {
ui = await import(resolve(__dirname, `./presets/${defaults.preset}`))
}
addTemplate({
filename: 'ui.mjs',
getContents: () => `/* @unocss-include */ export default ${JSON.stringify(ui)}`
})
addComponentsDir({
path: resolve(__dirname, './components/elements'),
prefix,
@@ -183,4 +219,7 @@ declare module '@nuxt/schema' {
interface NuxtConfig {
ui?: UiOptions
}
interface NuxtOptions {
ui?: UiOptions
}
}

96
src/presets/tailwindui.ts Normal file
View File

@@ -0,0 +1,96 @@
const input = {
wrapper: 'relative',
base: 'block w-full u-bg-white u-text-gray-700 disabled:cursor-not-allowed disabled:opacity-75 focus:outline-none',
size: {
xxs: 'text-xs',
xs: 'text-xs',
sm: 'text-sm leading-4',
md: 'text-sm',
lg: 'text-base',
xl: 'text-base'
},
spacing: {
xxs: 'px-1 py-0.5',
xs: 'px-2.5 py-1.5',
sm: 'px-3 py-2',
md: 'px-4 py-2',
lg: 'px-4 py-2',
xl: 'px-6 py-3'
},
leading: {
spacing: {
xxs: 'pl-7',
xs: 'pl-7',
sm: 'pl-10',
md: 'pl-10',
lg: 'pl-10',
xl: 'pl-10'
}
},
trailing: {
spacing: {
xxs: 'pr-7',
xs: 'pr-7',
sm: 'pr-10',
md: 'pr-10',
lg: 'pr-10',
xl: 'pr-10'
}
},
appearance: {
default: 'focus:ring-1 focus:ring-primary-500 focus:border-primary-500 border u-border-gray-300 rounded-md shadow-sm',
none: 'border-0 bg-transparent focus:ring-0 focus:shadow-none'
},
icon: {
base: 'u-text-gray-400',
size: {
xxs: 'h-3 w-3',
xs: 'h-4 w-4',
sm: 'h-5 w-5',
md: 'h-5 w-5',
lg: 'h-5 w-5',
xl: 'h-5 w-5'
},
leading: {
wrapper: 'absolute inset-y-0 left-0 flex items-center pointer-events-none',
spacing: {
xxs: 'ml-2',
xs: 'ml-2',
sm: 'ml-3',
md: 'ml-3',
lg: 'ml-3',
xl: 'ml-3'
}
},
trailing: {
wrapper: 'absolute inset-y-0 right-0 flex items-center pointer-events-none',
spacing: {
xxs: 'mr-2',
xs: 'mr-2',
sm: 'mr-3',
md: 'mr-3',
lg: 'mr-3',
xl: 'mr-3'
}
}
}
}
const textarea = {
...input
}
const select = {
...input
}
const button = {
base: ''
}
export default {
input,
textarea,
select,
button
}