mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-02-02 13:17:57 +01:00
feat!: handle color states on form elements (#234)
This commit is contained in:
@@ -5,7 +5,7 @@
|
|||||||
v-model="primary"
|
v-model="primary"
|
||||||
name="primary"
|
name="primary"
|
||||||
class="w-full [&>div>button]:!rounded-r-none"
|
class="w-full [&>div>button]:!rounded-r-none"
|
||||||
appearance="gray"
|
color="gray"
|
||||||
:ui="{ width: 'w-[194px]' }"
|
:ui="{ width: 'w-[194px]' }"
|
||||||
:popper="{ placement: 'bottom-start' }"
|
:popper="{ placement: 'bottom-start' }"
|
||||||
:options="primaryOptions"
|
:options="primaryOptions"
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
v-model="gray"
|
v-model="gray"
|
||||||
name="gray"
|
name="gray"
|
||||||
class="w-full [&>div>button]:!rounded-l-none [&>div>button]:-ml-px"
|
class="w-full [&>div>button]:!rounded-l-none [&>div>button]:-ml-px"
|
||||||
appearance="gray"
|
color="gray"
|
||||||
:ui="{ width: 'w-[194px]' }"
|
:ui="{ width: 'w-[194px]' }"
|
||||||
:popper="{ placement: 'bottom-end' }"
|
:popper="{ placement: 'bottom-end' }"
|
||||||
:options="grayOptions"
|
:options="grayOptions"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
v-if="prop.type === 'boolean'"
|
v-if="prop.type === 'boolean'"
|
||||||
v-model="componentProps[prop.name]"
|
v-model="componentProps[prop.name]"
|
||||||
:name="prop.name"
|
:name="prop.name"
|
||||||
appearance="none"
|
variant="none"
|
||||||
class="justify-center"
|
class="justify-center"
|
||||||
/>
|
/>
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
:options="prop.options"
|
:options="prop.options"
|
||||||
:name="prop.name"
|
:name="prop.name"
|
||||||
:label="componentProps[prop.name]"
|
:label="componentProps[prop.name]"
|
||||||
appearance="none"
|
variant="none"
|
||||||
class="inline-flex"
|
class="inline-flex"
|
||||||
:ui="{ width: 'w-32 !-mt-px', rounded: 'rounded-b-md' }"
|
:ui="{ width: 'w-32 !-mt-px', rounded: 'rounded-b-md' }"
|
||||||
:ui-select="{ custom: '!py-0' }"
|
:ui-select="{ custom: '!py-0' }"
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
:model-value="componentProps[prop.name]"
|
:model-value="componentProps[prop.name]"
|
||||||
:type="prop.type === 'number' ? 'number' : 'text'"
|
:type="prop.type === 'number' ? 'number' : 'text'"
|
||||||
:name="prop.name"
|
:name="prop.name"
|
||||||
appearance="none"
|
variant="none"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
:ui="{ custom: '!py-0' }"
|
:ui="{ custom: '!py-0' }"
|
||||||
@update:model-value="val => componentProps[prop.name] = prop.type === 'number' ? Number(val) : val"
|
@update:model-value="val => componentProps[prop.name] = prop.type === 'number' ? Number(val) : val"
|
||||||
@@ -79,6 +79,10 @@ const props = defineProps({
|
|||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => []
|
||||||
},
|
},
|
||||||
|
extraColors: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
backgroundClass: {
|
backgroundClass: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'bg-white dark:bg-gray-900'
|
default: 'bg-white dark:bg-gray-900'
|
||||||
@@ -121,7 +125,8 @@ const propsToSelect = computed(() => Object.keys(componentProps).map((key) => {
|
|||||||
const keys = useGet(ui.value, dottedKey, {})
|
const keys = useGet(ui.value, dottedKey, {})
|
||||||
let options = typeof keys === 'object' && Object.keys(keys)
|
let options = typeof keys === 'object' && Object.keys(keys)
|
||||||
if (key.toLowerCase().endsWith('color')) {
|
if (key.toLowerCase().endsWith('color')) {
|
||||||
options = appConfig.ui.colors
|
// @ts-ignore
|
||||||
|
options = [...appConfig.ui.colors, ...props.extraColors]
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ Use the `chip-color` and `chip-position` props to display a chip on the Avatar.
|
|||||||
props:
|
props:
|
||||||
chipColor: 'primary'
|
chipColor: 'primary'
|
||||||
chipPosition: 'top-right'
|
chipPosition: 'top-right'
|
||||||
|
extraColors:
|
||||||
|
- gray
|
||||||
baseProps:
|
baseProps:
|
||||||
src: 'https://avatars.githubusercontent.com/u/739984?v=4'
|
src: 'https://avatars.githubusercontent.com/u/739984?v=4'
|
||||||
alt: 'Avatar'
|
alt: 'Avatar'
|
||||||
|
|||||||
@@ -12,6 +12,53 @@ baseProps:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
### Style
|
||||||
|
|
||||||
|
Use the `color` and `variant` props to change the visual style of the Input.
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
baseProps:
|
||||||
|
name: 'input'
|
||||||
|
placeholder: 'Search...'
|
||||||
|
props:
|
||||||
|
color: 'primary'
|
||||||
|
variant: 'outline'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
Besides all the colors from the `ui.colors` object, you can also use the `white` (default) and `gray` colors with their pre-defined variants.
|
||||||
|
|
||||||
|
#### White
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
baseProps:
|
||||||
|
name: 'input'
|
||||||
|
placeholder: 'Search...'
|
||||||
|
props:
|
||||||
|
color: 'white'
|
||||||
|
variant: 'outline'
|
||||||
|
excludedProps:
|
||||||
|
- color
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
#### Gray
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
baseProps:
|
||||||
|
name: 'input'
|
||||||
|
placeholder: 'Search...'
|
||||||
|
props:
|
||||||
|
color: 'gray'
|
||||||
|
variant: 'outline'
|
||||||
|
excludedProps:
|
||||||
|
- color
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
### Size
|
### Size
|
||||||
|
|
||||||
Use the `size` prop to change the size of the Input.
|
Use the `size` prop to change the size of the Input.
|
||||||
@@ -38,20 +85,6 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### Appearance
|
|
||||||
|
|
||||||
Use the `appearance` prop to change the style of the Input.
|
|
||||||
|
|
||||||
::component-card
|
|
||||||
---
|
|
||||||
baseProps:
|
|
||||||
name: 'input'
|
|
||||||
placeholder: 'Search...'
|
|
||||||
props:
|
|
||||||
appearance: 'white'
|
|
||||||
---
|
|
||||||
::
|
|
||||||
|
|
||||||
### Icon
|
### Icon
|
||||||
|
|
||||||
Use any icon from [Iconify](https://icones.js.org) by setting the `icon` prop by using this pattern: `i-{collection_name}-{icon_name}`.
|
Use any icon from [Iconify](https://icones.js.org) by setting the `icon` prop by using this pattern: `i-{collection_name}-{icon_name}`.
|
||||||
@@ -65,9 +98,12 @@ baseProps:
|
|||||||
placeholder: 'Search...'
|
placeholder: 'Search...'
|
||||||
props:
|
props:
|
||||||
icon: 'i-heroicons-magnifying-glass-20-solid'
|
icon: 'i-heroicons-magnifying-glass-20-solid'
|
||||||
appearance: 'white'
|
|
||||||
size: 'sm'
|
size: 'sm'
|
||||||
|
color: 'white'
|
||||||
trailing: false
|
trailing: false
|
||||||
|
extraColors:
|
||||||
|
- white
|
||||||
|
- gray
|
||||||
excludedProps:
|
excludedProps:
|
||||||
- icon
|
- icon
|
||||||
---
|
---
|
||||||
@@ -81,12 +117,9 @@ Use the `disabled` prop to disable the Input.
|
|||||||
---
|
---
|
||||||
baseProps:
|
baseProps:
|
||||||
name: 'input'
|
name: 'input'
|
||||||
props:
|
|
||||||
placeholder: 'Search...'
|
placeholder: 'Search...'
|
||||||
appearance: 'white'
|
props:
|
||||||
disabled: true
|
disabled: true
|
||||||
excludedProps:
|
|
||||||
- placeholder
|
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
@@ -109,32 +142,6 @@ excludedProps:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### Group
|
|
||||||
|
|
||||||
You can use the `InputGroup` component to add a label and additional informations to a form element.
|
|
||||||
|
|
||||||
::component-card{slug="InputGroup"}
|
|
||||||
---
|
|
||||||
baseProps:
|
|
||||||
name: 'group'
|
|
||||||
props:
|
|
||||||
label: 'Email'
|
|
||||||
help: "We'll only use this for spam."
|
|
||||||
hint: 'Required'
|
|
||||||
required: true
|
|
||||||
code: >-
|
|
||||||
|
|
||||||
<UInput placeholder="you@example.com" icon="i-heroicons-envelope" />
|
|
||||||
---
|
|
||||||
|
|
||||||
#default
|
|
||||||
:u-input{name="group" placeholder="you@example.com" icon="i-heroicons-envelope"}
|
|
||||||
::
|
|
||||||
|
|
||||||
::alert{icon="i-heroicons-light-bulb"}
|
|
||||||
This also works with `Textarea`, `Select` and `SelectMenu` components.
|
|
||||||
::
|
|
||||||
|
|
||||||
## Props
|
## Props
|
||||||
|
|
||||||
:component-props
|
:component-props
|
||||||
|
|||||||
@@ -12,6 +12,53 @@ baseProps:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
### Style
|
||||||
|
|
||||||
|
Use the `color` and `variant` props to change the visual style of the Textarea.
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
baseProps:
|
||||||
|
name: 'textarea'
|
||||||
|
placeholder: 'Search...'
|
||||||
|
props:
|
||||||
|
color: 'primary'
|
||||||
|
variant: 'outline'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
Besides all the colors from the `ui.colors` object, you can also use the `white` (default) and `gray` colors with their pre-defined variants.
|
||||||
|
|
||||||
|
#### White
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
baseProps:
|
||||||
|
name: 'textarea'
|
||||||
|
placeholder: 'Search...'
|
||||||
|
props:
|
||||||
|
color: 'white'
|
||||||
|
variant: 'outline'
|
||||||
|
excludedProps:
|
||||||
|
- color
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
#### Gray
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
baseProps:
|
||||||
|
name: 'textarea'
|
||||||
|
placeholder: 'Search...'
|
||||||
|
props:
|
||||||
|
color: 'gray'
|
||||||
|
variant: 'outline'
|
||||||
|
excludedProps:
|
||||||
|
- color
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
### Size
|
### Size
|
||||||
|
|
||||||
Use the `size` prop to change the size of the Textarea.
|
Use the `size` prop to change the size of the Textarea.
|
||||||
@@ -38,20 +85,6 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### Appearance
|
|
||||||
|
|
||||||
Use the `appearance` prop to change the style of the Textarea.
|
|
||||||
|
|
||||||
::component-card
|
|
||||||
---
|
|
||||||
baseProps:
|
|
||||||
name: 'textarea'
|
|
||||||
placeholder: 'Search...'
|
|
||||||
props:
|
|
||||||
appearance: 'white'
|
|
||||||
---
|
|
||||||
::
|
|
||||||
|
|
||||||
### Disabled
|
### Disabled
|
||||||
|
|
||||||
Use the `disabled` prop to disable the Textarea.
|
Use the `disabled` prop to disable the Textarea.
|
||||||
@@ -62,7 +95,6 @@ baseProps:
|
|||||||
name: 'input'
|
name: 'input'
|
||||||
placeholder: 'Search...'
|
placeholder: 'Search...'
|
||||||
props:
|
props:
|
||||||
appearance: 'white'
|
|
||||||
disabled: true
|
disabled: true
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|||||||
@@ -22,9 +22,65 @@ excludedProps:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
### Style
|
||||||
|
|
||||||
|
Use the `color` and `variant` props to change the visual style of the Select.
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
baseProps:
|
||||||
|
name: 'select'
|
||||||
|
options:
|
||||||
|
- 'United States'
|
||||||
|
- 'Canada'
|
||||||
|
- 'Mexico'
|
||||||
|
props:
|
||||||
|
color: 'primary'
|
||||||
|
variant: 'outline'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
Besides all the colors from the `ui.colors` object, you can also use the `white` (default) and `gray` colors with their pre-defined variants.
|
||||||
|
|
||||||
|
#### White
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
baseProps:
|
||||||
|
name: 'select'
|
||||||
|
options:
|
||||||
|
- 'United States'
|
||||||
|
- 'Canada'
|
||||||
|
- 'Mexico'
|
||||||
|
props:
|
||||||
|
color: 'white'
|
||||||
|
variant: 'outline'
|
||||||
|
excludedProps:
|
||||||
|
- color
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
#### Gray
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
baseProps:
|
||||||
|
name: 'select'
|
||||||
|
options:
|
||||||
|
- 'United States'
|
||||||
|
- 'Canada'
|
||||||
|
- 'Mexico'
|
||||||
|
props:
|
||||||
|
color: 'gray'
|
||||||
|
variant: 'outline'
|
||||||
|
excludedProps:
|
||||||
|
- color
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
### Size
|
### Size
|
||||||
|
|
||||||
Use the `size` prop to change the size of the Input.
|
Use the `size` prop to change the size of the Select.
|
||||||
|
|
||||||
::component-card
|
::component-card
|
||||||
---
|
---
|
||||||
@@ -56,24 +112,6 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
### Appearance
|
|
||||||
|
|
||||||
Use the `appearance` prop to change the style of the Select.
|
|
||||||
|
|
||||||
::component-card
|
|
||||||
---
|
|
||||||
baseProps:
|
|
||||||
name: 'select'
|
|
||||||
options:
|
|
||||||
- 'United States'
|
|
||||||
- 'Canada'
|
|
||||||
- 'Mexico'
|
|
||||||
placeholder: 'Search...'
|
|
||||||
props:
|
|
||||||
appearance: 'white'
|
|
||||||
---
|
|
||||||
::
|
|
||||||
|
|
||||||
### Icon
|
### Icon
|
||||||
|
|
||||||
Use any icon from [Iconify](https://icones.js.org) by setting the `icon` prop by using this pattern: `i-{collection_name}-{icon_name}`.
|
Use any icon from [Iconify](https://icones.js.org) by setting the `icon` prop by using this pattern: `i-{collection_name}-{icon_name}`.
|
||||||
@@ -91,8 +129,11 @@ baseProps:
|
|||||||
placeholder: 'Search...'
|
placeholder: 'Search...'
|
||||||
props:
|
props:
|
||||||
icon: 'i-heroicons-magnifying-glass-20-solid'
|
icon: 'i-heroicons-magnifying-glass-20-solid'
|
||||||
appearance: 'white'
|
color: 'white'
|
||||||
size: 'sm'
|
size: 'sm'
|
||||||
|
extraColors:
|
||||||
|
- white
|
||||||
|
- gray
|
||||||
excludedProps:
|
excludedProps:
|
||||||
- icon
|
- icon
|
||||||
---
|
---
|
||||||
@@ -100,7 +141,7 @@ excludedProps:
|
|||||||
|
|
||||||
### Disabled
|
### Disabled
|
||||||
|
|
||||||
Use the `disabled` prop to disable the Input.
|
Use the `disabled` prop to disable the Select.
|
||||||
|
|
||||||
::component-card
|
::component-card
|
||||||
---
|
---
|
||||||
@@ -112,7 +153,6 @@ baseProps:
|
|||||||
- 'Mexico'
|
- 'Mexico'
|
||||||
placeholder: 'Search...'
|
placeholder: 'Search...'
|
||||||
props:
|
props:
|
||||||
appearance: 'white'
|
|
||||||
disabled: true
|
disabled: true
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ headlessui:
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
The SelectMenu component renders by default a [Select](/forms/select) component and is based on the `ui.select` preset. You can use most of the Select props to configure the display if you don't want to override the default slot such as [size](/forms/select#size), [placeholder](/forms/select#placeholder), [appearance](/forms/select#appearance), [icon](/forms/select#icon), [disabled](/forms/select#disabled), etc.
|
The SelectMenu component renders by default a [Select](/forms/select) component and is based on the `ui.select` preset. You can use most of the Select props to configure the display if you don't want to override the default slot such as [color](/forms/select#style), [variant](/forms/select#style), [size](/forms/select#size), [placeholder](/forms/select#placeholder), [icon](/forms/select#icon), [disabled](/forms/select#disabled), etc.
|
||||||
|
|
||||||
Like the Select component, you can use the `options` prop to pass an array of strings or objects.
|
Like the Select component, you can use the `options` prop to pass an array of strings or objects.
|
||||||
|
|
||||||
@@ -148,6 +148,10 @@ baseProps:
|
|||||||
options: ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
|
options: ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
|
||||||
props:
|
props:
|
||||||
icon: 'i-heroicons-magnifying-glass-20-solid'
|
icon: 'i-heroicons-magnifying-glass-20-solid'
|
||||||
|
color: 'white'
|
||||||
|
extraColors:
|
||||||
|
- white
|
||||||
|
- gray
|
||||||
excludedProps:
|
excludedProps:
|
||||||
- icon
|
- icon
|
||||||
---
|
---
|
||||||
@@ -157,6 +161,8 @@ excludedProps:
|
|||||||
|
|
||||||
Use the `searchable` prop to enable search.
|
Use the `searchable` prop to enable search.
|
||||||
|
|
||||||
|
Use the `searchable-placeholder` prop to set a different placeholder.
|
||||||
|
|
||||||
This will use Headless UI [Combobox](https://headlessui.com/vue/combobox) component instead of [Listbox](https://headlessui.com/vue/listbox).
|
This will use Headless UI [Combobox](https://headlessui.com/vue/combobox) component instead of [Listbox](https://headlessui.com/vue/listbox).
|
||||||
|
|
||||||
::component-card
|
::component-card
|
||||||
@@ -167,6 +173,7 @@ baseProps:
|
|||||||
options: ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
|
options: ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
|
||||||
props:
|
props:
|
||||||
searchable: true
|
searchable: true
|
||||||
|
searchablePlaceholder: 'Search a person...'
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
|||||||
140
docs/content/3.forms/8.form-group.md
Normal file
140
docs/content/3.forms/8.form-group.md
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
---
|
||||||
|
github: true
|
||||||
|
description: Display a label and additional informations around a form element.
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Use the FormGroup component around an [Input](/forms/input), [Textarea](/forms/textarea), [Select](/forms/select) or a [SelectMenu](/forms/select-menu) with the `name` prop to automatically associate a `<label>` element with the form element.
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
props:
|
||||||
|
name: 'email'
|
||||||
|
label: 'Email'
|
||||||
|
code: >-
|
||||||
|
|
||||||
|
<UInput placeholder="you@example.com" icon="i-heroicons-envelope" />
|
||||||
|
---
|
||||||
|
|
||||||
|
#default
|
||||||
|
:u-input{placeholder="you@example.com" icon="i-heroicons-envelope"}
|
||||||
|
::
|
||||||
|
|
||||||
|
### Required
|
||||||
|
|
||||||
|
Use the `required` prop to indicate that the form element is required.
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
baseProps:
|
||||||
|
name: 'group-required'
|
||||||
|
props:
|
||||||
|
label: 'Email'
|
||||||
|
required: true
|
||||||
|
code: >-
|
||||||
|
|
||||||
|
<UInput placeholder="you@example.com" icon="i-heroicons-envelope" />
|
||||||
|
---
|
||||||
|
|
||||||
|
#default
|
||||||
|
:u-input{placeholder="you@example.com" icon="i-heroicons-envelope"}
|
||||||
|
::
|
||||||
|
|
||||||
|
### Description
|
||||||
|
|
||||||
|
Use the `description` prop to display a description below the label.
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
baseProps:
|
||||||
|
name: 'group-description'
|
||||||
|
props:
|
||||||
|
label: 'Email'
|
||||||
|
description: "We'll only use this for spam."
|
||||||
|
code: >-
|
||||||
|
|
||||||
|
<UInput placeholder="you@example.com" icon="i-heroicons-envelope" />
|
||||||
|
---
|
||||||
|
|
||||||
|
#default
|
||||||
|
:u-input{placeholder="you@example.com" icon="i-heroicons-envelope"}
|
||||||
|
::
|
||||||
|
|
||||||
|
### Hint
|
||||||
|
|
||||||
|
Use the `hint` prop to display a hint above the form element.
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
baseProps:
|
||||||
|
name: 'group-hint'
|
||||||
|
props:
|
||||||
|
label: 'Email'
|
||||||
|
hint: 'Optional'
|
||||||
|
code: >-
|
||||||
|
|
||||||
|
<UInput placeholder="you@example.com" icon="i-heroicons-envelope" />
|
||||||
|
---
|
||||||
|
|
||||||
|
#default
|
||||||
|
:u-input{placeholder="you@example.com" icon="i-heroicons-envelope"}
|
||||||
|
::
|
||||||
|
|
||||||
|
### Help
|
||||||
|
|
||||||
|
Use the `help` prop to display an help message below the form element.
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
baseProps:
|
||||||
|
name: 'group-help'
|
||||||
|
props:
|
||||||
|
label: 'Email'
|
||||||
|
help: 'We will never share your email with anyone else.'
|
||||||
|
code: >-
|
||||||
|
|
||||||
|
<UInput placeholder="you@example.com" icon="i-heroicons-envelope" />
|
||||||
|
---
|
||||||
|
|
||||||
|
#default
|
||||||
|
:u-input{placeholder="you@example.com" icon="i-heroicons-envelope"}
|
||||||
|
::
|
||||||
|
|
||||||
|
### Error
|
||||||
|
|
||||||
|
Use the `error` prop to display an error message below the form element.
|
||||||
|
|
||||||
|
When used together with the `help` prop, the `error` prop will take precedence.
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
baseProps:
|
||||||
|
name: 'group-error'
|
||||||
|
props:
|
||||||
|
label: 'Email'
|
||||||
|
help: 'We will never share your email with anyone else.'
|
||||||
|
error: "Not a valid email address."
|
||||||
|
code: >-
|
||||||
|
|
||||||
|
<UInput placeholder="you@example.com" trailing-icon="i-heroicons-exclamation-triangle-20-solid" />
|
||||||
|
---
|
||||||
|
|
||||||
|
#default
|
||||||
|
:u-input{model-value="benjamincanac" placeholder="you@example.com" trailing-icon="i-heroicons-exclamation-triangle-20-solid"}
|
||||||
|
::
|
||||||
|
|
||||||
|
You can also use the `error` prop as a boolean to mark the form element as invalid.
|
||||||
|
|
||||||
|
::alert{icon="i-heroicons-light-bulb"}
|
||||||
|
The `error` prop will automatically set the `color` prop of the form element to `red`.
|
||||||
|
::
|
||||||
|
|
||||||
|
## Props
|
||||||
|
|
||||||
|
:component-props
|
||||||
|
|
||||||
|
## Preset
|
||||||
|
|
||||||
|
:component-preset
|
||||||
@@ -132,6 +132,8 @@ baseProps:
|
|||||||
props:
|
props:
|
||||||
icon: 'i-heroicons-x-circle'
|
icon: 'i-heroicons-x-circle'
|
||||||
color: 'red'
|
color: 'red'
|
||||||
|
extraColors:
|
||||||
|
- gray
|
||||||
excludedProps:
|
excludedProps:
|
||||||
- icon
|
- icon
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -121,45 +121,49 @@ export default defineNuxtModule<ModuleOptions>({
|
|||||||
}
|
}
|
||||||
|
|
||||||
tailwindConfig.safelist = tailwindConfig.safelist || []
|
tailwindConfig.safelist = tailwindConfig.safelist || []
|
||||||
tailwindConfig.safelist.push(...['bg-gray-400', {
|
tailwindConfig.safelist.push(...[
|
||||||
pattern: new RegExp(`bg-(${safeColorsAsRegex})-(50|400|500)`)
|
'bg-gray-500',
|
||||||
}, {
|
'dark:bg-gray-400',
|
||||||
pattern: new RegExp(`bg-(${safeColorsAsRegex})-500`),
|
{
|
||||||
variants: ['disabled']
|
pattern: new RegExp(`bg-(${safeColorsAsRegex})-(50|400|500)`)
|
||||||
}, {
|
}, {
|
||||||
pattern: new RegExp(`bg-(${safeColorsAsRegex})-(400|950)`),
|
pattern: new RegExp(`bg-(${safeColorsAsRegex})-500`),
|
||||||
variants: ['dark']
|
variants: ['disabled']
|
||||||
}, {
|
}, {
|
||||||
pattern: new RegExp(`bg-(${safeColorsAsRegex})-(500|900|950)`),
|
pattern: new RegExp(`bg-(${safeColorsAsRegex})-(400|950)`),
|
||||||
variants: ['dark:hover']
|
variants: ['dark']
|
||||||
}, {
|
}, {
|
||||||
pattern: new RegExp(`bg-(${safeColorsAsRegex})-400`),
|
pattern: new RegExp(`bg-(${safeColorsAsRegex})-(500|900|950)`),
|
||||||
variants: ['dark:disabled']
|
variants: ['dark:hover']
|
||||||
}, {
|
}, {
|
||||||
pattern: new RegExp(`bg-(${safeColorsAsRegex})-(50|100|600)`),
|
pattern: new RegExp(`bg-(${safeColorsAsRegex})-400`),
|
||||||
variants: ['hover']
|
variants: ['dark:disabled']
|
||||||
}, {
|
}, {
|
||||||
pattern: new RegExp(`outline-(${safeColorsAsRegex})-500`),
|
pattern: new RegExp(`bg-(${safeColorsAsRegex})-(50|100|600)`),
|
||||||
variants: ['focus-visible']
|
variants: ['hover']
|
||||||
}, {
|
}, {
|
||||||
pattern: new RegExp(`outline-(${safeColorsAsRegex})-400`),
|
pattern: new RegExp(`outline-(${safeColorsAsRegex})-500`),
|
||||||
variants: ['dark:focus-visible']
|
variants: ['focus-visible']
|
||||||
}, {
|
}, {
|
||||||
pattern: new RegExp(`ring-(${safeColorsAsRegex})-500`),
|
pattern: new RegExp(`outline-(${safeColorsAsRegex})-400`),
|
||||||
variants: ['focus-visible']
|
variants: ['dark:focus-visible']
|
||||||
}, {
|
}, {
|
||||||
pattern: new RegExp(`ring-(${safeColorsAsRegex})-400`),
|
pattern: new RegExp(`ring-(${safeColorsAsRegex})-500`),
|
||||||
variants: ['dark', 'dark:focus-visible']
|
variants: ['focus', 'focus-visible']
|
||||||
}, {
|
}, {
|
||||||
pattern: new RegExp(`text-(${safeColorsAsRegex})-400`),
|
pattern: new RegExp(`ring-(${safeColorsAsRegex})-400`),
|
||||||
variants: ['dark']
|
variants: ['dark', 'dark:focus', 'dark:focus-visible']
|
||||||
}, {
|
}, {
|
||||||
pattern: new RegExp(`text-(${safeColorsAsRegex})-600`),
|
pattern: new RegExp(`text-(${safeColorsAsRegex})-400`),
|
||||||
variants: ['hover']
|
variants: ['dark']
|
||||||
}, {
|
}, {
|
||||||
pattern: new RegExp(`text-(${safeColorsAsRegex})-500`),
|
pattern: new RegExp(`text-(${safeColorsAsRegex})-500`),
|
||||||
variants: ['dark:hover']
|
variants: ['dark:hover']
|
||||||
}])
|
}, {
|
||||||
|
pattern: new RegExp(`text-(${safeColorsAsRegex})-600`),
|
||||||
|
variants: ['hover']
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
tailwindConfig.plugins = tailwindConfig.plugins || []
|
tailwindConfig.plugins = tailwindConfig.plugins || []
|
||||||
tailwindConfig.plugins.push(iconsPlugin({ collections: getIconCollections(options.icons as any[]) }))
|
tailwindConfig.plugins.push(iconsPlugin({ collections: getIconCollections(options.icons as any[]) }))
|
||||||
|
|||||||
@@ -218,7 +218,9 @@ const kbd = {
|
|||||||
|
|
||||||
const input = {
|
const input = {
|
||||||
wrapper: 'relative',
|
wrapper: 'relative',
|
||||||
base: 'relative block w-full disabled:cursor-not-allowed disabled:opacity-75 focus:outline-none',
|
base: 'relative block w-full disabled:cursor-not-allowed disabled:opacity-75 focus:outline-none border-0',
|
||||||
|
rounded: 'rounded-md',
|
||||||
|
placeholder: 'placeholder-gray-400 dark:placeholder-gray-500',
|
||||||
custom: '',
|
custom: '',
|
||||||
size: {
|
size: {
|
||||||
'2xs': 'text-xs',
|
'2xs': 'text-xs',
|
||||||
@@ -264,13 +266,21 @@ const input = {
|
|||||||
xl: 'pr-12'
|
xl: 'pr-12'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
appearance: {
|
color: {
|
||||||
white: 'border-0 bg-white dark:bg-gray-900 text-gray-900 dark:text-white rounded-md shadow-sm ring-1 ring-inset ring-gray-300 dark:ring-gray-700 focus:ring-2 focus:ring-primary-500 dark:focus:ring-primary-400 placeholder:text-gray-400 dark:placeholder:text-gray-500',
|
white: {
|
||||||
gray: 'border-0 bg-gray-50 dark:bg-gray-800 text-gray-900 dark:text-white rounded-md shadow-sm ring-1 ring-inset ring-gray-300 dark:ring-gray-700 focus:ring-2 focus:ring-primary-500 dark:focus:ring-primary-400 placeholder:text-gray-400 dark:placeholder:text-gray-500',
|
outline: 'shadow-sm bg-white dark:bg-gray-900 text-gray-900 dark:text-white ring-1 ring-inset ring-gray-300 dark:ring-gray-700 focus:ring-2 focus:ring-primary-500 dark:focus:ring-primary-400',
|
||||||
none: 'border-0 bg-transparent focus:ring-0 focus:shadow-none placeholder:text-gray-400 dark:placeholder:text-gray-500'
|
},
|
||||||
|
gray: {
|
||||||
|
outline: 'shadow-sm bg-gray-50 dark:bg-gray-800 text-gray-900 dark:text-white ring-1 ring-inset ring-gray-300 dark:ring-gray-700 focus:ring-2 focus:ring-primary-500 dark:focus:ring-primary-400',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
variant: {
|
||||||
|
outline: 'shadow-sm bg-transparent text-gray-900 dark:text-white ring-1 ring-inset ring-{color}-500 dark:ring-{color}-400 focus:ring-2 focus:ring-{color}-500 dark:focus:ring-{color}-400',
|
||||||
|
none: 'bg-transparent focus:ring-0 focus:shadow-none'
|
||||||
},
|
},
|
||||||
icon: {
|
icon: {
|
||||||
base: 'text-gray-400 dark:text-gray-500',
|
base: 'flex-shrink-0 text-gray-400 dark:text-gray-500',
|
||||||
|
color: 'text-{color}-500 dark:text-{color}-400',
|
||||||
size: {
|
size: {
|
||||||
'2xs': 'h-3.5 w-3.5',
|
'2xs': 'h-3.5 w-3.5',
|
||||||
xs: 'h-4 w-4',
|
xs: 'h-4 w-4',
|
||||||
@@ -304,27 +314,32 @@ const input = {
|
|||||||
},
|
},
|
||||||
default: {
|
default: {
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
appearance: 'white',
|
color: 'white',
|
||||||
|
variant: 'outline',
|
||||||
loadingIcon: 'i-heroicons-arrow-path-20-solid'
|
loadingIcon: 'i-heroicons-arrow-path-20-solid'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const inputGroup = {
|
const formGroup = {
|
||||||
wrapper: '',
|
wrapper: '',
|
||||||
label: 'block text-sm font-medium text-gray-700 dark:text-gray-200',
|
label: {
|
||||||
labelWrapper: 'flex content-center justify-between',
|
wrapper: 'flex content-center justify-between',
|
||||||
|
base: 'block text-sm font-medium text-gray-700 dark:text-gray-200',
|
||||||
|
required: `after:content-['*'] after:ml-0.5 after:text-red-500 dark:after:text-red-400`
|
||||||
|
},
|
||||||
|
description: 'text-sm text-gray-500 dark:text-gray-400',
|
||||||
container: 'mt-1 relative',
|
container: 'mt-1 relative',
|
||||||
required: 'text-red-500 dark:text-red-400 ml-0.5',
|
hint: 'text-sm text-gray-500 dark:text-gray-400',
|
||||||
description: 'text-sm leading-5 text-gray-500 dark:text-gray-400',
|
help: 'mt-2 text-sm text-gray-500 dark:text-gray-400',
|
||||||
hint: 'text-sm leading-5 text-gray-500 dark:text-gray-400',
|
error: 'mt-2 text-sm text-red-500 dark:text-red-400'
|
||||||
help: 'mt-2 text-sm text-gray-500 dark:text-gray-400'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const textarea = {
|
const textarea = {
|
||||||
...input,
|
...input,
|
||||||
default: {
|
default: {
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
appearance: 'white'
|
color: 'white',
|
||||||
|
variant: 'outline',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,7 +347,8 @@ const select = {
|
|||||||
...input,
|
...input,
|
||||||
default: {
|
default: {
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
appearance: 'white',
|
color: 'white',
|
||||||
|
variant: 'outline',
|
||||||
trailingIcon: 'i-heroicons-chevron-down-20-solid'
|
trailingIcon: 'i-heroicons-chevron-down-20-solid'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -701,7 +717,7 @@ const notification = {
|
|||||||
padding: 'p-4',
|
padding: 'p-4',
|
||||||
ring: 'ring-1 ring-gray-200 dark:ring-gray-800',
|
ring: 'ring-1 ring-gray-200 dark:ring-gray-800',
|
||||||
icon: {
|
icon: {
|
||||||
base: 'flex-shrink-0 w-5 h-5',
|
base: 'flex-shrink-0 w-5 h-5 text-gray-400 dark:text-gray-500',
|
||||||
color: 'text-{color}-500 dark:text-{color}-400'
|
color: 'text-{color}-500 dark:text-{color}-400'
|
||||||
},
|
},
|
||||||
avatar: {
|
avatar: {
|
||||||
@@ -753,7 +769,7 @@ export default {
|
|||||||
dropdown,
|
dropdown,
|
||||||
kbd,
|
kbd,
|
||||||
input,
|
input,
|
||||||
inputGroup,
|
formGroup,
|
||||||
textarea,
|
textarea,
|
||||||
select,
|
select,
|
||||||
selectMenu,
|
selectMenu,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { h, computed, defineComponent } from 'vue'
|
import { h, computed, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import { classNames } from '../../utils'
|
import { classNames, getSlotsChildren } from '../../utils'
|
||||||
import Avatar from './Avatar.vue'
|
import Avatar from './Avatar.vue'
|
||||||
import { useAppConfig } from '#imports'
|
import { useAppConfig } from '#imports'
|
||||||
// TODO: Remove
|
// TODO: Remove
|
||||||
@@ -34,20 +34,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
const ui = computed<Partial<typeof appConfig.ui.avatarGroup>>(() => defu({}, props.ui, appConfig.ui.avatarGroup))
|
const ui = computed<Partial<typeof appConfig.ui.avatarGroup>>(() => defu({}, props.ui, appConfig.ui.avatarGroup))
|
||||||
|
|
||||||
const children = computed(() => {
|
const children = computed(() => getSlotsChildren(slots))
|
||||||
let children = slots.default?.()
|
|
||||||
if (children.length) {
|
|
||||||
if (typeof children[0].type === 'symbol') {
|
|
||||||
// @ts-ignore-next
|
|
||||||
children = children[0].children
|
|
||||||
// @ts-ignore-next
|
|
||||||
} else if (children[0].type.name === 'ContentSlot') {
|
|
||||||
// @ts-ignore-next
|
|
||||||
children = children[0].ctx.slots.default?.()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return children
|
|
||||||
})
|
|
||||||
|
|
||||||
const max = computed(() => typeof props.max === 'string' ? parseInt(props.max, 10) : props.max)
|
const max = computed(() => typeof props.max === 'string' ? parseInt(props.max, 10) : props.max)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { h, computed, defineComponent } from 'vue'
|
import { h, computed, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
|
import { getSlotsChildren } from '../../utils'
|
||||||
import { useAppConfig } from '#imports'
|
import { useAppConfig } from '#imports'
|
||||||
// TODO: Remove
|
// TODO: Remove
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
@@ -28,20 +29,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
const ui = computed<Partial<typeof appConfig.ui.buttonGroup>>(() => defu({}, props.ui, appConfig.ui.buttonGroup))
|
const ui = computed<Partial<typeof appConfig.ui.buttonGroup>>(() => defu({}, props.ui, appConfig.ui.buttonGroup))
|
||||||
|
|
||||||
const children = computed(() => {
|
const children = computed(() => getSlotsChildren(slots))
|
||||||
let children = slots.default?.()
|
|
||||||
if (children.length) {
|
|
||||||
if (typeof children[0].type === 'symbol') {
|
|
||||||
// @ts-ignore-next
|
|
||||||
children = children[0].children
|
|
||||||
// @ts-ignore-next
|
|
||||||
} else if (children[0].type.name === 'ContentSlot') {
|
|
||||||
// @ts-ignore-next
|
|
||||||
children = children[0].ctx.slots.default?.()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return children
|
|
||||||
})
|
|
||||||
|
|
||||||
const rounded = computed(() => ({
|
const rounded = computed(() => ({
|
||||||
'rounded-none': { left: 'rounded-l-none', right: 'rounded-r-none' },
|
'rounded-none': { left: 'rounded-l-none', right: 'rounded-r-none' },
|
||||||
|
|||||||
82
src/runtime/components/forms/FormGroup.ts
Normal file
82
src/runtime/components/forms/FormGroup.ts
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import { computed, defineComponent } from 'vue'
|
||||||
|
import type { PropType } from 'vue'
|
||||||
|
import { defu } from 'defu'
|
||||||
|
import { getSlotsChildren } from '../../utils'
|
||||||
|
import { useAppConfig } from '#imports'
|
||||||
|
// TODO: Remove
|
||||||
|
// @ts-expect-error
|
||||||
|
import appConfig from '#build/app.config'
|
||||||
|
|
||||||
|
// const appConfig = useAppConfig()
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
required: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
help: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
type: [String, Boolean],
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
hint: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
ui: {
|
||||||
|
type: Object as PropType<Partial<typeof appConfig.ui.formGroup>>,
|
||||||
|
default: () => appConfig.ui.formGroup
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup (props, { slots }) {
|
||||||
|
// TODO: Remove
|
||||||
|
const appConfig = useAppConfig()
|
||||||
|
|
||||||
|
const ui = computed<Partial<typeof appConfig.ui.formGroup>>(() => defu({}, props.ui, appConfig.ui.formGroup))
|
||||||
|
|
||||||
|
const children = computed(() => getSlotsChildren(slots))
|
||||||
|
|
||||||
|
const clones = computed(() => children.value.map((node) => {
|
||||||
|
if (props.error) {
|
||||||
|
node.props.oldColor = node.props.color
|
||||||
|
node.props.color = 'red'
|
||||||
|
} else {
|
||||||
|
node.props.color = node.props.oldColor
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.name) {
|
||||||
|
node.props.name = props.name
|
||||||
|
}
|
||||||
|
|
||||||
|
return node
|
||||||
|
}))
|
||||||
|
|
||||||
|
return () => h('div', { class: [ui.value.wrapper] }, [
|
||||||
|
props.label && h('div', { class: [ui.value.label.wrapper] }, [
|
||||||
|
h('label', { for: props.name, class: [ui.value.label.base, props.required && ui.value.label.required] }, props.label),
|
||||||
|
props.hint && h('span', { class: [ui.value.hint] }, props.hint)
|
||||||
|
]),
|
||||||
|
props.description && h('p', { class: [ui.value.description] }, props.description),
|
||||||
|
h('div', { class: [!!props.label && ui.value.container] }, [
|
||||||
|
...clones.value,
|
||||||
|
props.error && typeof props.error === 'string' ? h('p', { class: [ui.value.error] }, props.error) : props.help ? h('p', { class: [ui.value.help] }, props.help) : null
|
||||||
|
])
|
||||||
|
])
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -113,6 +113,10 @@ export default defineComponent({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
|
padded: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => appConfig.ui.input.default.size,
|
default: () => appConfig.ui.input.default.size,
|
||||||
@@ -120,11 +124,21 @@ export default defineComponent({
|
|||||||
return Object.keys(appConfig.ui.input.size).includes(value)
|
return Object.keys(appConfig.ui.input.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
appearance: {
|
color: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => appConfig.ui.input.default.appearance,
|
default: () => appConfig.ui.input.default.color,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return Object.keys(appConfig.ui.input.appearance).includes(value)
|
return [...appConfig.ui.colors, ...Object.keys(appConfig.ui.input.color)].includes(value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
variant: {
|
||||||
|
type: String,
|
||||||
|
default: () => appConfig.ui.input.default.variant,
|
||||||
|
validator (value: string) {
|
||||||
|
return [
|
||||||
|
...Object.keys(appConfig.ui.input.variant),
|
||||||
|
...Object.values(appConfig.ui.input.color).flatMap(value => Object.keys(value))
|
||||||
|
].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
@@ -158,11 +172,15 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const inputClass = computed(() => {
|
const inputClass = computed(() => {
|
||||||
|
const variant = ui.value.color?.[props.color as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
||||||
|
|
||||||
return classNames(
|
return classNames(
|
||||||
ui.value.base,
|
ui.value.base,
|
||||||
|
ui.value.rounded,
|
||||||
|
ui.value.placeholder,
|
||||||
ui.value.size[props.size],
|
ui.value.size[props.size],
|
||||||
ui.value.padding[props.size],
|
props.padded && ui.value.padding[props.size],
|
||||||
ui.value.appearance[props.appearance],
|
variant?.replaceAll('{color}', props.color),
|
||||||
isLeading.value && ui.value.leading.padding[props.size],
|
isLeading.value && ui.value.leading.padding[props.size],
|
||||||
isTrailing.value && ui.value.trailing.padding[props.size],
|
isTrailing.value && ui.value.trailing.padding[props.size],
|
||||||
ui.value.custom
|
ui.value.custom
|
||||||
@@ -196,6 +214,7 @@ export default defineComponent({
|
|||||||
const iconClass = computed(() => {
|
const iconClass = computed(() => {
|
||||||
return classNames(
|
return classNames(
|
||||||
ui.value.icon.base,
|
ui.value.icon.base,
|
||||||
|
appConfig.ui.colors.includes(props.color) && ui.value.icon.color.replaceAll('{color}', props.color),
|
||||||
ui.value.icon.size[props.size],
|
ui.value.icon.size[props.size],
|
||||||
props.loading && 'animate-spin'
|
props.loading && 'animate-spin'
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div :class="ui.wrapper">
|
|
||||||
<div v-if="label || $slots.label" :class="ui.labelWrapper">
|
|
||||||
<label :for="name" :class="ui.label">
|
|
||||||
<slot name="label">{{ label }}</slot>
|
|
||||||
<span v-if="required" :class="ui.required">*</span>
|
|
||||||
</label>
|
|
||||||
<span v-if="$slots.hint || hint" :class="ui.hint">
|
|
||||||
<slot name="hint">{{ hint }}</slot>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<p v-if="description" :class="ui.description">
|
|
||||||
{{ description }}
|
|
||||||
</p>
|
|
||||||
<div :class="!!label && ui.container">
|
|
||||||
<slot />
|
|
||||||
<p v-if="help" :class="ui.help">
|
|
||||||
{{ help }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { computed, defineComponent } from 'vue'
|
|
||||||
import type { PropType } from 'vue'
|
|
||||||
import { defu } from 'defu'
|
|
||||||
import { useAppConfig } from '#imports'
|
|
||||||
// TODO: Remove
|
|
||||||
// @ts-expect-error
|
|
||||||
import appConfig from '#build/app.config'
|
|
||||||
|
|
||||||
// const appConfig = useAppConfig()
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
props: {
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
required: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
help: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
hint: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
ui: {
|
|
||||||
type: Object as PropType<Partial<typeof appConfig.ui.inputGroup>>,
|
|
||||||
default: () => appConfig.ui.inputGroup
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setup (props) {
|
|
||||||
// TODO: Remove
|
|
||||||
const appConfig = useAppConfig()
|
|
||||||
|
|
||||||
const ui = computed<Partial<typeof appConfig.ui.inputGroup>>(() => defu({}, props.ui, appConfig.ui.inputGroup))
|
|
||||||
|
|
||||||
return {
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
|
||||||
ui
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
@@ -97,6 +97,10 @@ export default defineComponent({
|
|||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => []
|
||||||
},
|
},
|
||||||
|
padded: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => appConfig.ui.select.default.size,
|
default: () => appConfig.ui.select.default.size,
|
||||||
@@ -104,11 +108,21 @@ export default defineComponent({
|
|||||||
return Object.keys(appConfig.ui.select.size).includes(value)
|
return Object.keys(appConfig.ui.select.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
appearance: {
|
color: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => appConfig.ui.select.default.appearance,
|
default: () => appConfig.ui.select.default.color,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return Object.keys(appConfig.ui.select.appearance).includes(value)
|
return [...appConfig.ui.colors, ...Object.keys(appConfig.ui.select.color)].includes(value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
variant: {
|
||||||
|
type: String,
|
||||||
|
default: () => appConfig.ui.select.default.variant,
|
||||||
|
validator (value: string) {
|
||||||
|
return [
|
||||||
|
...Object.keys(appConfig.ui.select.variant),
|
||||||
|
...Object.values(appConfig.ui.select.color).flatMap(value => Object.keys(value))
|
||||||
|
].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
textAttribute: {
|
textAttribute: {
|
||||||
@@ -188,11 +202,15 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const selectClass = computed(() => {
|
const selectClass = computed(() => {
|
||||||
|
const variant = ui.value.color?.[props.color as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
||||||
|
|
||||||
return classNames(
|
return classNames(
|
||||||
ui.value.base,
|
ui.value.base,
|
||||||
|
ui.value.rounded,
|
||||||
|
ui.value.placeholder,
|
||||||
ui.value.size[props.size],
|
ui.value.size[props.size],
|
||||||
ui.value.padding[props.size],
|
props.padded && ui.value.padding[props.size],
|
||||||
ui.value.appearance[props.appearance],
|
variant?.replaceAll('{color}', props.color),
|
||||||
!!props.icon && ui.value.leading.padding[props.size],
|
!!props.icon && ui.value.leading.padding[props.size],
|
||||||
ui.value.trailing.padding[props.size],
|
ui.value.trailing.padding[props.size],
|
||||||
ui.value.custom
|
ui.value.custom
|
||||||
@@ -202,6 +220,7 @@ export default defineComponent({
|
|||||||
const iconClass = computed(() => {
|
const iconClass = computed(() => {
|
||||||
return classNames(
|
return classNames(
|
||||||
ui.value.icon.base,
|
ui.value.icon.base,
|
||||||
|
appConfig.ui.colors.includes(props.color) && ui.value.icon.color.replaceAll('{color}', props.color),
|
||||||
ui.value.icon.size[props.size]
|
ui.value.icon.size[props.size]
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -198,6 +198,10 @@ export default defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
|
padded: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => appConfig.ui.select.default.size,
|
default: () => appConfig.ui.select.default.size,
|
||||||
@@ -205,11 +209,21 @@ export default defineComponent({
|
|||||||
return Object.keys(appConfig.ui.select.size).includes(value)
|
return Object.keys(appConfig.ui.select.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
appearance: {
|
color: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => appConfig.ui.select.default.appearance,
|
default: () => appConfig.ui.select.default.color,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return Object.keys(appConfig.ui.select.appearance).includes(value)
|
return [...appConfig.ui.colors, ...Object.keys(appConfig.ui.select.color)].includes(value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
variant: {
|
||||||
|
type: String,
|
||||||
|
default: () => appConfig.ui.select.default.variant,
|
||||||
|
validator (value: string) {
|
||||||
|
return [
|
||||||
|
...Object.keys(appConfig.ui.select.variant),
|
||||||
|
...Object.values(appConfig.ui.select.color).flatMap(value => Object.keys(value))
|
||||||
|
].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
optionAttribute: {
|
optionAttribute: {
|
||||||
@@ -249,13 +263,17 @@ export default defineComponent({
|
|||||||
const searchInput = ref<ComponentPublicInstance<HTMLElement>>()
|
const searchInput = ref<ComponentPublicInstance<HTMLElement>>()
|
||||||
|
|
||||||
const selectMenuClass = computed(() => {
|
const selectMenuClass = computed(() => {
|
||||||
|
const variant = uiSelect.value.color?.[props.color as string]?.[props.variant as string] || uiSelect.value.variant[props.variant]
|
||||||
|
|
||||||
return classNames(
|
return classNames(
|
||||||
uiSelect.value.base,
|
uiSelect.value.base,
|
||||||
|
uiSelect.value.rounded,
|
||||||
|
uiSelect.value.placeholder,
|
||||||
'text-left cursor-default',
|
'text-left cursor-default',
|
||||||
uiSelect.value.size[props.size],
|
uiSelect.value.size[props.size],
|
||||||
uiSelect.value.gap[props.size],
|
uiSelect.value.gap[props.size],
|
||||||
uiSelect.value.padding[props.size],
|
props.padded && uiSelect.value.padding[props.size],
|
||||||
uiSelect.value.appearance[props.appearance],
|
variant?.replaceAll('{color}', props.color),
|
||||||
!!props.icon && uiSelect.value.leading.padding[props.size],
|
!!props.icon && uiSelect.value.leading.padding[props.size],
|
||||||
uiSelect.value.trailing.padding[props.size],
|
uiSelect.value.trailing.padding[props.size],
|
||||||
uiSelect.value.custom,
|
uiSelect.value.custom,
|
||||||
@@ -266,6 +284,7 @@ export default defineComponent({
|
|||||||
const iconClass = computed(() => {
|
const iconClass = computed(() => {
|
||||||
return classNames(
|
return classNames(
|
||||||
uiSelect.value.icon.base,
|
uiSelect.value.icon.base,
|
||||||
|
appConfig.ui.colors.includes(props.color) && uiSelect.value.icon.color.replaceAll('{color}', props.color),
|
||||||
uiSelect.value.icon.size[props.size]
|
uiSelect.value.icon.size[props.size]
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -72,6 +72,10 @@ export default defineComponent({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
|
padded: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => appConfig.ui.textarea.default.size,
|
default: () => appConfig.ui.textarea.default.size,
|
||||||
@@ -79,11 +83,21 @@ export default defineComponent({
|
|||||||
return Object.keys(appConfig.ui.textarea.size).includes(value)
|
return Object.keys(appConfig.ui.textarea.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
appearance: {
|
color: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => appConfig.ui.textarea.default.appearance,
|
default: () => appConfig.ui.textarea.default.color,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return Object.keys(appConfig.ui.textarea.appearance).includes(value)
|
return [...appConfig.ui.colors, ...Object.keys(appConfig.ui.textarea.color)].includes(value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
variant: {
|
||||||
|
type: String,
|
||||||
|
default: () => appConfig.ui.textarea.default.variant,
|
||||||
|
validator (value: string) {
|
||||||
|
return [
|
||||||
|
...Object.keys(appConfig.ui.textarea.variant),
|
||||||
|
...Object.values(appConfig.ui.textarea.color).flatMap(value => Object.keys(value))
|
||||||
|
].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
@@ -146,11 +160,15 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const textareaClass = computed(() => {
|
const textareaClass = computed(() => {
|
||||||
|
const variant = ui.value.color?.[props.color as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
||||||
|
|
||||||
return classNames(
|
return classNames(
|
||||||
ui.value.base,
|
ui.value.base,
|
||||||
|
ui.value.rounded,
|
||||||
|
ui.value.placeholder,
|
||||||
ui.value.size[props.size],
|
ui.value.size[props.size],
|
||||||
ui.value.padding[props.size],
|
props.padded && ui.value.padding[props.size],
|
||||||
ui.value.appearance[props.appearance],
|
variant?.replaceAll('{color}', props.color),
|
||||||
!props.resize && 'resize-none',
|
!props.resize && 'resize-none',
|
||||||
ui.value.custom
|
ui.value.custom
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ export default defineComponent({
|
|||||||
const iconClass = computed(() => {
|
const iconClass = computed(() => {
|
||||||
return classNames(
|
return classNames(
|
||||||
ui.value.icon.base,
|
ui.value.icon.base,
|
||||||
ui.value.icon.color?.replaceAll('{color}', props.color)
|
appConfig.ui.colors.includes(props.color) && ui.value.icon.color?.replaceAll('{color}', props.color)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -14,3 +14,18 @@ export const omit = (obj: object, keys: string[]) => {
|
|||||||
Object.entries(obj).filter(([key]) => !keys.includes(key))
|
Object.entries(obj).filter(([key]) => !keys.includes(key))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getSlotsChildren = (slots: any) => {
|
||||||
|
let children = slots.default?.()
|
||||||
|
if (children.length) {
|
||||||
|
if (typeof children[0].type === 'symbol') {
|
||||||
|
// @ts-ignore-next
|
||||||
|
children = children[0].children
|
||||||
|
// @ts-ignore-next
|
||||||
|
} else if (children[0].type.name === 'ContentSlot') {
|
||||||
|
// @ts-ignore-next
|
||||||
|
children = children[0].ctx.slots.default?.()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return children
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user