mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-31 12:17:54 +01:00
chore: migrate components to typescript setup (#55)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
This commit is contained in:
committed by
GitHub
parent
6c2f93f262
commit
39bf242f78
@@ -292,7 +292,9 @@ const dropdownItems = [
|
|||||||
icon: 'heroicons-solid:archive'
|
icon: 'heroicons-solid:archive'
|
||||||
}, {
|
}, {
|
||||||
label: 'Move',
|
label: 'Move',
|
||||||
icon: 'heroicons-solid:external-link'
|
icon: 'heroicons-solid:external-link',
|
||||||
|
to: 'https://www.google.fr',
|
||||||
|
target: '_blank'
|
||||||
}],
|
}],
|
||||||
[{
|
[{
|
||||||
label: 'Delete',
|
label: 'Delete',
|
||||||
|
|||||||
@@ -76,7 +76,8 @@ const components = [
|
|||||||
{
|
{
|
||||||
label: 'AvatarGroup',
|
label: 'AvatarGroup',
|
||||||
to: '/components/AvatarGroup',
|
to: '/components/AvatarGroup',
|
||||||
nuxt3: true
|
nuxt3: true,
|
||||||
|
typescript: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Badge',
|
label: 'Badge',
|
||||||
@@ -91,14 +92,16 @@ const components = [
|
|||||||
to: '/components/Button',
|
to: '/components/Button',
|
||||||
nuxt3: true,
|
nuxt3: true,
|
||||||
capi: true,
|
capi: true,
|
||||||
preset: true
|
preset: true,
|
||||||
|
typescript: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Dropdown',
|
label: 'Dropdown',
|
||||||
to: '/components/Dropdown',
|
to: '/components/Dropdown',
|
||||||
nuxt3: true,
|
nuxt3: true,
|
||||||
capi: true,
|
capi: true,
|
||||||
preset: true
|
preset: true,
|
||||||
|
typescript: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Icon',
|
label: 'Icon',
|
||||||
@@ -111,7 +114,8 @@ const components = [
|
|||||||
label: 'Link',
|
label: 'Link',
|
||||||
to: '/components/Link',
|
to: '/components/Link',
|
||||||
nuxt3: true,
|
nuxt3: true,
|
||||||
capi: true
|
capi: true,
|
||||||
|
typescript: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Toggle',
|
label: 'Toggle',
|
||||||
@@ -124,7 +128,8 @@ const components = [
|
|||||||
{
|
{
|
||||||
label: 'Alert',
|
label: 'Alert',
|
||||||
to: '/components/Alert',
|
to: '/components/Alert',
|
||||||
nuxt3: true
|
nuxt3: true,
|
||||||
|
typescript: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'AlertDialog',
|
label: 'AlertDialog',
|
||||||
@@ -138,35 +143,40 @@ const components = [
|
|||||||
label: 'Input',
|
label: 'Input',
|
||||||
to: '/components/Input',
|
to: '/components/Input',
|
||||||
capi: true,
|
capi: true,
|
||||||
preset: true
|
preset: true,
|
||||||
|
typescript: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'FormGroup',
|
label: 'FormGroup',
|
||||||
to: '/components/FormGroup',
|
to: '/components/FormGroup',
|
||||||
nuxt3: true,
|
nuxt3: true,
|
||||||
capi: true,
|
capi: true,
|
||||||
preset: true
|
preset: true,
|
||||||
|
typescript: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Checkbox',
|
label: 'Checkbox',
|
||||||
to: '/components/Checkbox',
|
to: '/components/Checkbox',
|
||||||
nuxt3: true,
|
nuxt3: true,
|
||||||
capi: true,
|
capi: true,
|
||||||
preset: true
|
preset: true,
|
||||||
|
typescript: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Radio',
|
label: 'Radio',
|
||||||
to: '/components/Radio',
|
to: '/components/Radio',
|
||||||
nuxt3: true,
|
nuxt3: true,
|
||||||
capi: true,
|
capi: true,
|
||||||
preset: true
|
preset: true,
|
||||||
|
typescript: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Select',
|
label: 'Select',
|
||||||
to: '/components/Select',
|
to: '/components/Select',
|
||||||
nuxt3: true,
|
nuxt3: true,
|
||||||
capi: true,
|
capi: true,
|
||||||
preset: true
|
preset: true,
|
||||||
|
typescript: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'SelectCustom',
|
label: 'SelectCustom',
|
||||||
@@ -178,14 +188,16 @@ const components = [
|
|||||||
to: '/components/Textarea',
|
to: '/components/Textarea',
|
||||||
nuxt3: true,
|
nuxt3: true,
|
||||||
capi: true,
|
capi: true,
|
||||||
preset: true
|
preset: true,
|
||||||
|
typescript: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Card',
|
label: 'Card',
|
||||||
to: '/components/Card',
|
to: '/components/Card',
|
||||||
nuxt3: true,
|
nuxt3: true,
|
||||||
capi: true,
|
capi: true,
|
||||||
preset: true
|
preset: true,
|
||||||
|
typescript: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Container',
|
label: 'Container',
|
||||||
@@ -216,20 +228,23 @@ const components = [
|
|||||||
to: '/components/VerticalNavigation',
|
to: '/components/VerticalNavigation',
|
||||||
nuxt3: true,
|
nuxt3: true,
|
||||||
capi: true,
|
capi: true,
|
||||||
preset: true
|
preset: true,
|
||||||
|
typescript: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Modal',
|
label: 'Modal',
|
||||||
to: '/components/Modal',
|
to: '/components/Modal',
|
||||||
nuxt3: true,
|
nuxt3: true,
|
||||||
preset: true,
|
preset: true,
|
||||||
capi: true
|
capi: true,
|
||||||
|
typescript: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Notification',
|
label: 'Notification',
|
||||||
to: '/components/Notification',
|
to: '/components/Notification',
|
||||||
nuxt3: true,
|
nuxt3: true,
|
||||||
capi: true
|
capi: true,
|
||||||
|
typescript: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Notifications',
|
label: 'Notifications',
|
||||||
@@ -242,7 +257,8 @@ const components = [
|
|||||||
label: 'Popover',
|
label: 'Popover',
|
||||||
to: '/components/Popover',
|
to: '/components/Popover',
|
||||||
nuxt3: true,
|
nuxt3: true,
|
||||||
capi: true
|
capi: true,
|
||||||
|
typescript: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Slideover',
|
label: 'Slideover',
|
||||||
@@ -255,7 +271,8 @@ const components = [
|
|||||||
label: 'Tooltip',
|
label: 'Tooltip',
|
||||||
to: '/components/Tooltip',
|
to: '/components/Tooltip',
|
||||||
nuxt3: true,
|
nuxt3: true,
|
||||||
capi: true
|
capi: true,
|
||||||
|
typescript: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<Avatar
|
<Avatar
|
||||||
v-for="(avatar, index) of avatars"
|
v-for="(avatar, index) of displayedGroup"
|
||||||
:key="index"
|
:key="index"
|
||||||
v-bind="avatar"
|
v-bind="avatar"
|
||||||
class="ring-2 u-ring-white -ml-1.5 first:ml-0"
|
class="ring-2 u-ring-white -ml-1.5 first:ml-0"
|
||||||
@@ -16,46 +16,43 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
import Avatar from './Avatar'
|
import Avatar from './Avatar'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
components: {
|
group: {
|
||||||
Avatar
|
type: Array,
|
||||||
|
default: () => []
|
||||||
},
|
},
|
||||||
props: {
|
size: {
|
||||||
group: {
|
type: String,
|
||||||
type: Array,
|
default: 'md',
|
||||||
default: () => []
|
validator (value: string) {
|
||||||
},
|
return ['xxxs', 'xxs', 'xs', 'sm', 'md', 'lg', 'xl'].includes(value)
|
||||||
size: {
|
|
||||||
type: String,
|
|
||||||
default: 'md',
|
|
||||||
validator (value) {
|
|
||||||
return ['xxxs', 'xxs', 'xs', 'sm', 'md', 'lg', 'xl'].includes(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
max: {
|
|
||||||
type: Number,
|
|
||||||
default: null
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
max: {
|
||||||
avatars () {
|
type: Number,
|
||||||
return this.group.map((avatar) => {
|
default: null
|
||||||
return typeof avatar === 'string' ? { src: avatar } : avatar
|
|
||||||
})
|
|
||||||
},
|
|
||||||
displayedGroup () {
|
|
||||||
if (!this.max) { return this.avatars }
|
|
||||||
|
|
||||||
return this.avatars.slice(0, this.max)
|
|
||||||
},
|
|
||||||
remainingGroupSize () {
|
|
||||||
if (!this.max) { return 0 }
|
|
||||||
|
|
||||||
return this.avatars.length - this.max
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
|
const avatars = computed(() => {
|
||||||
|
return props.group.map((avatar) => {
|
||||||
|
return typeof avatar === 'string' ? { src: avatar } : avatar
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const displayedGroup = computed(() => {
|
||||||
|
if (!props.max) { return avatars.value }
|
||||||
|
|
||||||
|
return avatars.value.slice(0, props.max)
|
||||||
|
})
|
||||||
|
|
||||||
|
const remainingGroupSize = computed(() => {
|
||||||
|
if (!props.max) { return 0 }
|
||||||
|
|
||||||
|
return avatars.value.length - props.max
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -12,174 +12,159 @@
|
|||||||
</component>
|
</component>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed, useSlots } from 'vue'
|
||||||
import Link from '../elements/Link'
|
|
||||||
import Icon from '../elements/Icon'
|
import Icon from '../elements/Icon'
|
||||||
import { classNames } from '../../utils'
|
import { classNames } from '../../utils'
|
||||||
|
import NuxtLink from '#app/components/nuxt-link'
|
||||||
import $ui from '#build/ui'
|
import $ui from '#build/ui'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
components: {
|
type: {
|
||||||
Icon,
|
type: String,
|
||||||
Link
|
default: 'button'
|
||||||
},
|
},
|
||||||
props: {
|
block: {
|
||||||
type: {
|
type: Boolean,
|
||||||
type: String,
|
default: false
|
||||||
default: 'button'
|
},
|
||||||
},
|
label: {
|
||||||
block: {
|
type: String,
|
||||||
type: Boolean,
|
default: null
|
||||||
default: false
|
},
|
||||||
},
|
loading: {
|
||||||
label: {
|
type: Boolean,
|
||||||
type: String,
|
default: false
|
||||||
default: null
|
},
|
||||||
},
|
disabled: {
|
||||||
loading: {
|
type: Boolean,
|
||||||
type: Boolean,
|
default: false
|
||||||
default: false
|
},
|
||||||
},
|
size: {
|
||||||
disabled: {
|
type: String,
|
||||||
type: Boolean,
|
default: 'md',
|
||||||
default: false
|
validator (value: string) {
|
||||||
},
|
return Object.keys($ui.button.size).includes(value)
|
||||||
size: {
|
|
||||||
type: String,
|
|
||||||
default: 'md',
|
|
||||||
validator (value) {
|
|
||||||
return Object.keys($ui.button.size).includes(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
variant: {
|
|
||||||
type: String,
|
|
||||||
default: 'primary',
|
|
||||||
validator (value) {
|
|
||||||
return Object.keys($ui.button.variant).includes(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
icon: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
loadingIcon: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.button.icon.loading
|
|
||||||
},
|
|
||||||
trailing: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
leading: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
to: {
|
|
||||||
type: [String, Object],
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
target: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
ariaLabel: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
rounded: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
baseClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.button.base
|
|
||||||
},
|
|
||||||
iconBaseClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.button.icon.base
|
|
||||||
},
|
|
||||||
customClass: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
square: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
truncate: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props, { slots }) {
|
variant: {
|
||||||
const button = ref(null)
|
type: String,
|
||||||
|
default: 'primary',
|
||||||
const buttonIs = computed(() => {
|
validator (value: string) {
|
||||||
if (props.to) {
|
return Object.keys($ui.button.variant).includes(value)
|
||||||
return 'Link'
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'button'
|
|
||||||
})
|
|
||||||
|
|
||||||
const buttonProps = computed(() => {
|
|
||||||
switch (buttonIs.value) {
|
|
||||||
case 'Link': return { to: props.to, target: props.target }
|
|
||||||
default: return { disabled: props.disabled || props.loading, type: props.type }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const isLeading = computed(() => {
|
|
||||||
return (props.icon && props.leading) || (props.icon && !props.trailing) || (props.loading && !props.trailing)
|
|
||||||
})
|
|
||||||
|
|
||||||
const isTrailing = computed(() => {
|
|
||||||
return (props.icon && props.trailing) || (props.loading && props.trailing)
|
|
||||||
})
|
|
||||||
|
|
||||||
const isSquare = computed(() => props.square || (!slots.default && !props.label))
|
|
||||||
|
|
||||||
const buttonClass = computed(() => {
|
|
||||||
return classNames(
|
|
||||||
props.baseClass,
|
|
||||||
$ui.button.size[props.size],
|
|
||||||
$ui.button[isSquare.value ? 'square' : 'spacing'][props.size],
|
|
||||||
$ui.button.variant[props.variant],
|
|
||||||
props.block ? 'w-full flex justify-center items-center' : 'inline-flex items-center',
|
|
||||||
props.rounded ? 'rounded-full' : 'rounded-md',
|
|
||||||
props.customClass
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
const iconName = computed(() => {
|
|
||||||
if (props.loading) {
|
|
||||||
return props.loadingIcon
|
|
||||||
}
|
|
||||||
|
|
||||||
return props.icon
|
|
||||||
})
|
|
||||||
|
|
||||||
const iconClass = computed(() => {
|
|
||||||
return classNames(
|
|
||||||
props.iconBaseClass,
|
|
||||||
$ui.button.icon.size[props.size],
|
|
||||||
isLeading.value && (!!slots.default || !!props.label?.length) && $ui.button.icon.leading.spacing[props.size],
|
|
||||||
isTrailing.value && (!!slots.default || !!props.label?.length) && $ui.button.icon.trailing.spacing[props.size],
|
|
||||||
props.loading && 'animate-spin'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
button,
|
|
||||||
buttonIs,
|
|
||||||
buttonProps,
|
|
||||||
buttonClass,
|
|
||||||
isLeading,
|
|
||||||
isTrailing,
|
|
||||||
iconName,
|
|
||||||
iconClass
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
loadingIcon: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.button.icon.loading
|
||||||
|
},
|
||||||
|
trailing: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
leading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
to: {
|
||||||
|
type: [String, Object],
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
target: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
ariaLabel: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
rounded: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
baseClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.button.base
|
||||||
|
},
|
||||||
|
iconBaseClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.button.icon.base
|
||||||
|
},
|
||||||
|
customClass: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
square: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
truncate: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
|
const slots = useSlots()
|
||||||
|
|
||||||
|
const button = ref(null)
|
||||||
|
|
||||||
|
const buttonIs = computed(() => {
|
||||||
|
if (props.to) {
|
||||||
|
return NuxtLink
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'button'
|
||||||
|
})
|
||||||
|
|
||||||
|
const buttonProps = computed(() => {
|
||||||
|
if (props.to) {
|
||||||
|
return { to: props.to, target: props.target }
|
||||||
|
} else {
|
||||||
|
return { disabled: props.disabled || props.loading, type: props.type }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const isLeading = computed(() => {
|
||||||
|
return (props.icon && props.leading) || (props.icon && !props.trailing) || (props.loading && !props.trailing)
|
||||||
|
})
|
||||||
|
|
||||||
|
const isTrailing = computed(() => {
|
||||||
|
return (props.icon && props.trailing) || (props.loading && props.trailing)
|
||||||
|
})
|
||||||
|
|
||||||
|
const isSquare = computed(() => props.square || (!slots.default && !props.label))
|
||||||
|
|
||||||
|
const buttonClass = computed(() => {
|
||||||
|
return classNames(
|
||||||
|
props.baseClass,
|
||||||
|
$ui.button.size[props.size],
|
||||||
|
$ui.button[isSquare.value ? 'square' : 'spacing'][props.size],
|
||||||
|
$ui.button.variant[props.variant],
|
||||||
|
props.block ? 'w-full flex justify-center items-center' : 'inline-flex items-center',
|
||||||
|
props.rounded ? 'rounded-full' : 'rounded-md',
|
||||||
|
props.customClass
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const iconName = computed(() => {
|
||||||
|
if (props.loading) {
|
||||||
|
return props.loadingIcon
|
||||||
|
}
|
||||||
|
|
||||||
|
return props.icon
|
||||||
|
})
|
||||||
|
|
||||||
|
const iconClass = computed(() => {
|
||||||
|
return classNames(
|
||||||
|
props.iconBaseClass,
|
||||||
|
$ui.button.icon.size[props.size],
|
||||||
|
isLeading.value && (!!slots.default || !!props.label?.length) && $ui.button.icon.leading.spacing[props.size],
|
||||||
|
isTrailing.value && (!!slots.default || !!props.label?.length) && $ui.button.icon.trailing.spacing[props.size],
|
||||||
|
props.loading && 'animate-spin'
|
||||||
|
)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -18,8 +18,8 @@
|
|||||||
>
|
>
|
||||||
<MenuItems :class="baseClass" static>
|
<MenuItems :class="baseClass" static>
|
||||||
<div v-for="(subItems, index) of items" :key="index" class="py-1">
|
<div v-for="(subItems, index) of items" :key="index" class="py-1">
|
||||||
<MenuItem v-for="(item, subIndex) of subItems" :key="subIndex" v-slot="{ active, disabled }" :disabled="item.disabled">
|
<MenuItem v-for="(item, subIndex) of subItems" :key="subIndex" v-slot="{ active, disabled }" :disabled="item.disabled" as="div">
|
||||||
<Component v-bind="item" :is="(item.to && 'Link') || (item.click && 'button') || 'div'" :class="resolveItemClass({ active, disabled })" @click="onItemClick(item)" @mouseover="$emit('hover', item)">
|
<Component v-bind="item" :is="(item.to && NuxtLink) || (item.click && 'button') || 'div'" :class="resolveItemClass({ active, disabled })" @click="onItemClick(item)" @mouseover="$emit('hover', item)">
|
||||||
<slot :name="item.slot" :item="item">
|
<slot :name="item.slot" :item="item">
|
||||||
<Icon v-if="item.icon" :name="item.icon" :class="itemIconClass" />
|
<Icon v-if="item.icon" :name="item.icon" :class="itemIconClass" />
|
||||||
<Avatar v-if="item.avatar" :src="item.avatar" :alt="item.label" :class="itemAvatarClass" size="xs" />
|
<Avatar v-if="item.avatar" :src="item.avatar" :alt="item.label" :class="itemAvatarClass" size="xs" />
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
</Menu>
|
</Menu>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import {
|
import {
|
||||||
Menu,
|
Menu,
|
||||||
MenuButton,
|
MenuButton,
|
||||||
@@ -43,195 +43,176 @@ import {
|
|||||||
MenuItem
|
MenuItem
|
||||||
} from '@headlessui/vue'
|
} from '@headlessui/vue'
|
||||||
|
|
||||||
|
import type { Ref } from 'vue'
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import Icon from '../elements/Icon'
|
import Icon from '../elements/Icon'
|
||||||
import Avatar from '../elements/Avatar'
|
import Avatar from '../elements/Avatar'
|
||||||
import Link from '../elements/Link'
|
|
||||||
import { classNames, usePopper } from '../../utils'
|
import { classNames, usePopper } from '../../utils'
|
||||||
import $ui from '#build/ui'
|
import $ui from '#build/ui'
|
||||||
|
import NuxtLink from '#app/components/nuxt-link'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
components: {
|
items: {
|
||||||
Menu,
|
type: Array,
|
||||||
MenuButton,
|
default: () => []
|
||||||
MenuItems,
|
|
||||||
MenuItem,
|
|
||||||
Icon,
|
|
||||||
Avatar,
|
|
||||||
Link
|
|
||||||
},
|
},
|
||||||
props: {
|
placement: {
|
||||||
items: {
|
type: String,
|
||||||
type: Array,
|
default: 'bottom-end',
|
||||||
default: () => []
|
validator: (value: string) => {
|
||||||
},
|
return ['auto', 'auto-start', 'auto-end', 'top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'right', 'right-start', 'right-end', 'left', 'left-start', 'left-end'].includes(value)
|
||||||
placement: {
|
|
||||||
type: String,
|
|
||||||
default: 'bottom-end',
|
|
||||||
validator: (value) => {
|
|
||||||
return ['auto', 'auto-start', 'auto-end', 'top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'right', 'right-start', 'right-end', 'left', 'left-start', 'left-end'].includes(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
strategy: {
|
|
||||||
type: String,
|
|
||||||
default: 'fixed',
|
|
||||||
validator: (value) => {
|
|
||||||
return ['absolute', 'fixed'].includes(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mode: {
|
|
||||||
type: String,
|
|
||||||
default: 'click',
|
|
||||||
validator: (value) => {
|
|
||||||
return ['click', 'hover'].includes(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
wrapperClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.dropdown.wrapper
|
|
||||||
},
|
|
||||||
containerClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.dropdown.container
|
|
||||||
},
|
|
||||||
baseClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.dropdown.base
|
|
||||||
},
|
|
||||||
itemBaseClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.dropdown.item.base
|
|
||||||
},
|
|
||||||
itemActiveClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.dropdown.item.active
|
|
||||||
},
|
|
||||||
itemInactiveClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.dropdown.item.inactive
|
|
||||||
},
|
|
||||||
itemDisabledClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.dropdown.item.disabled
|
|
||||||
},
|
|
||||||
itemIconClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.dropdown.item.icon
|
|
||||||
},
|
|
||||||
itemAvatarClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.dropdown.item.avatar
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['hover'],
|
strategy: {
|
||||||
setup (props) {
|
type: String,
|
||||||
const [trigger, container] = usePopper({
|
default: 'fixed',
|
||||||
placement: props.placement,
|
validator: (value: string) => {
|
||||||
strategy: props.strategy,
|
return ['absolute', 'fixed'].includes(value)
|
||||||
modifiers: [{
|
|
||||||
name: 'offset',
|
|
||||||
options: {
|
|
||||||
offset: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'computeStyles',
|
|
||||||
options: {
|
|
||||||
gpuAcceleration: false,
|
|
||||||
adaptive: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'preventOverflow',
|
|
||||||
options: {
|
|
||||||
padding: 8
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
})
|
|
||||||
|
|
||||||
function resolveItemClass ({ active, disabled }) {
|
|
||||||
return classNames(
|
|
||||||
props.itemBaseClass,
|
|
||||||
active ? props.itemActiveClass : props.itemInactiveClass,
|
|
||||||
disabled && props.itemDisabledClass
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
function onItemClick (item) {
|
mode: {
|
||||||
if (item.disabled) {
|
type: String,
|
||||||
return
|
default: 'click',
|
||||||
}
|
validator: (value: string) => {
|
||||||
|
return ['click', 'hover'].includes(value)
|
||||||
if (item.click) {
|
|
||||||
item.click()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
wrapperClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.dropdown.wrapper
|
||||||
|
},
|
||||||
|
containerClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.dropdown.container
|
||||||
|
},
|
||||||
|
baseClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.dropdown.base
|
||||||
|
},
|
||||||
|
itemBaseClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.dropdown.item.base
|
||||||
|
},
|
||||||
|
itemActiveClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.dropdown.item.active
|
||||||
|
},
|
||||||
|
itemInactiveClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.dropdown.item.inactive
|
||||||
|
},
|
||||||
|
itemDisabledClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.dropdown.item.disabled
|
||||||
|
},
|
||||||
|
itemIconClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.dropdown.item.icon
|
||||||
|
},
|
||||||
|
itemAvatarClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.dropdown.item.avatar
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const menuApi = ref(null)
|
defineEmits(['hover'])
|
||||||
let openTimeout = null
|
|
||||||
let closeTimeout = null
|
|
||||||
onMounted(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
const menuProvides = trigger.value?.$.provides
|
|
||||||
const menuProvidesSymbols = Object.getOwnPropertySymbols(menuProvides)
|
|
||||||
menuApi.value = menuProvidesSymbols.length && menuProvides[menuProvidesSymbols[0]]
|
|
||||||
// stop trigger click propagation on hover
|
|
||||||
menuApi.value.buttonRef.addEventListener('click', (e) => {
|
|
||||||
if (props.mode === 'hover') {
|
|
||||||
e.stopPropagation()
|
|
||||||
}
|
|
||||||
}, true)
|
|
||||||
}, 0)
|
|
||||||
})
|
|
||||||
|
|
||||||
function onMouseOver () {
|
const [trigger, container] = usePopper({
|
||||||
if (props.mode !== 'hover' || !menuApi.value) {
|
placement: props.placement,
|
||||||
return
|
strategy: props.strategy,
|
||||||
}
|
modifiers: [{
|
||||||
|
name: 'offset',
|
||||||
// cancel programmed closing
|
options: {
|
||||||
if (closeTimeout) {
|
offset: 0
|
||||||
clearTimeout(closeTimeout)
|
|
||||||
closeTimeout = null
|
|
||||||
}
|
|
||||||
// dropdown already open
|
|
||||||
if (menuApi.value.menuState === 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
openTimeout = openTimeout || setTimeout(() => {
|
|
||||||
menuApi.value.openMenu && menuApi.value.openMenu()
|
|
||||||
openTimeout = null
|
|
||||||
}, 50)
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
function onMouseLeave () {
|
{
|
||||||
if (props.mode !== 'hover' || !menuApi.value) {
|
name: 'computeStyles',
|
||||||
return
|
options: {
|
||||||
}
|
gpuAcceleration: false,
|
||||||
|
adaptive: false
|
||||||
// cancel programmed opening
|
|
||||||
if (openTimeout) {
|
|
||||||
clearTimeout(openTimeout)
|
|
||||||
openTimeout = null
|
|
||||||
}
|
|
||||||
// dropdown already closed
|
|
||||||
if (menuApi.value.menuState === 1) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
closeTimeout = closeTimeout || setTimeout(() => {
|
|
||||||
menuApi.value.closeMenu && menuApi.value.closeMenu()
|
|
||||||
closeTimeout = null
|
|
||||||
}, 0)
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
return {
|
{
|
||||||
trigger,
|
name: 'preventOverflow',
|
||||||
container,
|
options: {
|
||||||
onItemClick,
|
padding: 8
|
||||||
onMouseOver,
|
|
||||||
onMouseLeave,
|
|
||||||
resolveItemClass
|
|
||||||
}
|
}
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
|
||||||
|
function resolveItemClass ({ active, disabled }: { active: boolean, disabled: boolean }) {
|
||||||
|
return classNames(
|
||||||
|
props.itemBaseClass,
|
||||||
|
active ? props.itemActiveClass : props.itemInactiveClass,
|
||||||
|
disabled && props.itemDisabledClass
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onItemClick (item: any) {
|
||||||
|
if (item.disabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.click) {
|
||||||
|
item.click()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const menuApi: Ref<any> = ref(null)
|
||||||
|
let openTimeout: NodeJS.Timeout | null = null
|
||||||
|
let closeTimeout: NodeJS.Timeout | null = null
|
||||||
|
onMounted(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
const menuProvides = trigger.value?.$.provides
|
||||||
|
const menuProvidesSymbols = Object.getOwnPropertySymbols(menuProvides)
|
||||||
|
menuApi.value = menuProvidesSymbols.length && menuProvides[menuProvidesSymbols[0]]
|
||||||
|
// stop trigger click propagation on hover
|
||||||
|
menuApi.value?.buttonRef.addEventListener('click', (e: Event) => {
|
||||||
|
if (props.mode === 'hover') {
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
}, true)
|
||||||
|
}, 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
function onMouseOver () {
|
||||||
|
if (props.mode !== 'hover' || !menuApi.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// cancel programmed closing
|
||||||
|
if (closeTimeout) {
|
||||||
|
clearTimeout(closeTimeout)
|
||||||
|
closeTimeout = null
|
||||||
|
}
|
||||||
|
// dropdown already open
|
||||||
|
if (menuApi.value.menuState === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
openTimeout = openTimeout || setTimeout(() => {
|
||||||
|
menuApi.value.openMenu && menuApi.value.openMenu()
|
||||||
|
openTimeout = null
|
||||||
|
}, 50)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMouseLeave () {
|
||||||
|
if (props.mode !== 'hover' || !menuApi.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// cancel programmed opening
|
||||||
|
if (openTimeout) {
|
||||||
|
clearTimeout(openTimeout)
|
||||||
|
openTimeout = null
|
||||||
|
}
|
||||||
|
// dropdown already closed
|
||||||
|
if (menuApi.value.menuState === 1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
closeTimeout = closeTimeout || setTimeout(() => {
|
||||||
|
menuApi.value.closeMenu && menuApi.value.closeMenu()
|
||||||
|
closeTimeout = null
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -22,53 +22,49 @@
|
|||||||
</router-link>
|
</router-link>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { RouterLink } from 'vue-router'
|
import { RouterLink } from 'vue-router'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
name: 'Link',
|
...RouterLink.props,
|
||||||
inheritAttrs: false,
|
to: {
|
||||||
props: {
|
type: [String, Object],
|
||||||
...RouterLink.props,
|
default: null
|
||||||
to: {
|
|
||||||
type: [String, Object],
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
inactiveClass: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
exact: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
target: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
setup (props) {
|
inactiveClass: {
|
||||||
const isExternalLink = computed(() => {
|
type: String,
|
||||||
return typeof props.to === 'string' && props.to.startsWith('http')
|
default: ''
|
||||||
})
|
},
|
||||||
const isButton = computed(() => {
|
exact: {
|
||||||
return !props.to
|
type: Boolean,
|
||||||
})
|
default: false
|
||||||
|
},
|
||||||
|
target: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
function resolveLinkClass ({ isActive, isExactActive }) {
|
const isExternalLink = computed(() => {
|
||||||
if (props.exact) {
|
return typeof props.to === 'string' && props.to.startsWith('http')
|
||||||
return isExactActive ? props.activeClass : props.inactiveClass
|
})
|
||||||
} else {
|
const isButton = computed(() => {
|
||||||
return isActive ? props.activeClass : props.inactiveClass
|
return !props.to
|
||||||
}
|
})
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
function resolveLinkClass ({ isActive, isExactActive }: { isActive: boolean, isExactActive: boolean }) {
|
||||||
isButton,
|
if (props.exact) {
|
||||||
isExternalLink,
|
return isExactActive ? props.activeClass : props.inactiveClass
|
||||||
resolveLinkClass
|
} else {
|
||||||
}
|
return isActive ? props.activeClass : props.inactiveClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'Link',
|
||||||
|
inheritAttrs: false
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -9,95 +9,92 @@
|
|||||||
{{ title }}
|
{{ title }}
|
||||||
</p>
|
</p>
|
||||||
<p v-if="link" class="mt-3 text-sm leading-5 md:mt-0 md:ml-6">
|
<p v-if="link" class="mt-3 text-sm leading-5 md:mt-0 md:ml-6">
|
||||||
<Link
|
<NuxtLink
|
||||||
:to="to"
|
:to="to"
|
||||||
class="whitespace-nowrap font-medium"
|
class="whitespace-nowrap font-medium"
|
||||||
:class="linkClass"
|
:class="linkClass"
|
||||||
@click="click && click()"
|
@click="click && click()"
|
||||||
>
|
>
|
||||||
{{ link }} →
|
{{ link }} →
|
||||||
</Link>
|
</NuxtLink>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
import Icon from '../elements/Icon'
|
import Icon from '../elements/Icon'
|
||||||
import Link from '../elements/Link'
|
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
components: {
|
variant: {
|
||||||
Icon,
|
type: String,
|
||||||
Link
|
default: 'info',
|
||||||
},
|
validator (value: string) {
|
||||||
props: {
|
return ['info', 'warning', 'error', 'success'].includes(value)
|
||||||
variant: {
|
|
||||||
type: String,
|
|
||||||
default: 'info',
|
|
||||||
validator (value) {
|
|
||||||
return ['info', 'warning', 'error', 'success'].includes(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
to: {
|
|
||||||
type: [String, Object],
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
click: {
|
|
||||||
type: Function,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
link: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
to: {
|
||||||
iconName () {
|
type: [String, Object],
|
||||||
return ({
|
default: null
|
||||||
info: 'heroicons-solid:information-circle',
|
},
|
||||||
warning: 'heroicons-solid:exclamation',
|
click: {
|
||||||
error: 'heroicons-solid:x-circle',
|
type: Function,
|
||||||
success: 'heroicons-solid:check-circle'
|
default: null
|
||||||
})[this.variant]
|
},
|
||||||
},
|
title: {
|
||||||
variantClass () {
|
type: String,
|
||||||
return ({
|
default: null
|
||||||
info: 'bg-blue-50',
|
},
|
||||||
warning: 'bg-orange-50',
|
link: {
|
||||||
error: 'bg-red-50',
|
type: String,
|
||||||
success: 'bg-green-50'
|
default: null
|
||||||
})[this.variant]
|
|
||||||
},
|
|
||||||
iconClass () {
|
|
||||||
return ({
|
|
||||||
info: 'text-blue-400',
|
|
||||||
warning: 'text-orange-400',
|
|
||||||
error: 'text-red-400',
|
|
||||||
success: 'text-green-400'
|
|
||||||
})[this.variant]
|
|
||||||
},
|
|
||||||
titleClass () {
|
|
||||||
return ({
|
|
||||||
info: 'text-blue-700',
|
|
||||||
warning: 'text-orange-700',
|
|
||||||
error: 'text-red-700',
|
|
||||||
success: 'text-green-700'
|
|
||||||
})[this.variant]
|
|
||||||
},
|
|
||||||
linkClass () {
|
|
||||||
return ({
|
|
||||||
info: 'text-blue-700 hover:text-blue-600',
|
|
||||||
warning: 'text-orange-700 hover:text-orange-600',
|
|
||||||
error: 'text-red-700 hover:text-red-600',
|
|
||||||
success: 'text-green-700 hover:text-green-600'
|
|
||||||
})[this.variant]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
|
const iconName = computed(() => {
|
||||||
|
return ({
|
||||||
|
info: 'heroicons-solid:information-circle',
|
||||||
|
warning: 'heroicons-solid:exclamation',
|
||||||
|
error: 'heroicons-solid:x-circle',
|
||||||
|
success: 'heroicons-solid:check-circle'
|
||||||
|
})[props.variant]
|
||||||
|
})
|
||||||
|
|
||||||
|
const variantClass = computed(() => {
|
||||||
|
return ({
|
||||||
|
info: 'bg-blue-50',
|
||||||
|
warning: 'bg-orange-50',
|
||||||
|
error: 'bg-red-50',
|
||||||
|
success: 'bg-green-50'
|
||||||
|
})[props.variant]
|
||||||
|
})
|
||||||
|
|
||||||
|
const iconClass = computed(() => {
|
||||||
|
return ({
|
||||||
|
info: 'text-blue-400',
|
||||||
|
warning: 'text-orange-400',
|
||||||
|
error: 'text-red-400',
|
||||||
|
success: 'text-green-400'
|
||||||
|
})[props.variant]
|
||||||
|
})
|
||||||
|
|
||||||
|
const titleClass = computed(() => {
|
||||||
|
return ({
|
||||||
|
info: 'text-blue-700',
|
||||||
|
warning: 'text-orange-700',
|
||||||
|
error: 'text-red-700',
|
||||||
|
success: 'text-green-700'
|
||||||
|
})[props.variant]
|
||||||
|
})
|
||||||
|
|
||||||
|
const linkClass = computed(() => {
|
||||||
|
return ({
|
||||||
|
info: 'text-blue-700 hover:text-blue-600',
|
||||||
|
warning: 'text-orange-700 hover:text-orange-600',
|
||||||
|
error: 'text-red-700 hover:text-red-600',
|
||||||
|
success: 'text-green-700 hover:text-green-600'
|
||||||
|
})[props.variant]
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -26,88 +26,81 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { classNames } from '../../utils'
|
import { classNames } from '../../utils'
|
||||||
import $ui from '#build/ui'
|
import $ui from '#build/ui'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
props: {
|
value: {
|
||||||
value: {
|
type: [String, Number, Boolean],
|
||||||
type: [String, Number, Boolean],
|
default: null
|
||||||
default: null
|
|
||||||
},
|
|
||||||
modelValue: {
|
|
||||||
type: [String, Number, Boolean, Array],
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
disabled: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
help: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
required: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
wrapperClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.checkbox.wrapper
|
|
||||||
},
|
|
||||||
baseClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.checkbox.base
|
|
||||||
},
|
|
||||||
labelClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.checkbox.label
|
|
||||||
},
|
|
||||||
requiredClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.checkbox.required
|
|
||||||
},
|
|
||||||
helpClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.checkbox.help
|
|
||||||
},
|
|
||||||
customClass: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'focus', 'blur'],
|
modelValue: {
|
||||||
setup (props, { emit }) {
|
type: [String, Number, Boolean, Array],
|
||||||
const isChecked = computed({
|
default: null
|
||||||
get () {
|
},
|
||||||
return props.modelValue
|
name: {
|
||||||
},
|
type: String,
|
||||||
set (value) {
|
default: null
|
||||||
emit('update:modelValue', value)
|
},
|
||||||
}
|
disabled: {
|
||||||
})
|
type: Boolean,
|
||||||
|
default: false
|
||||||
const inputClass = computed(() => {
|
},
|
||||||
return classNames(
|
help: {
|
||||||
props.baseClass,
|
type: String,
|
||||||
props.customClass
|
default: null
|
||||||
)
|
},
|
||||||
})
|
label: {
|
||||||
|
type: String,
|
||||||
return {
|
default: null
|
||||||
isChecked,
|
},
|
||||||
inputClass
|
required: {
|
||||||
}
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
wrapperClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.checkbox.wrapper
|
||||||
|
},
|
||||||
|
baseClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.checkbox.base
|
||||||
|
},
|
||||||
|
labelClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.checkbox.label
|
||||||
|
},
|
||||||
|
requiredClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.checkbox.required
|
||||||
|
},
|
||||||
|
helpClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.checkbox.help
|
||||||
|
},
|
||||||
|
customClass: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue', 'focus', 'blur'])
|
||||||
|
|
||||||
|
const isChecked = computed({
|
||||||
|
get () {
|
||||||
|
return props.modelValue
|
||||||
|
},
|
||||||
|
set (value) {
|
||||||
|
emit('update:modelValue', value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const inputClass = computed(() => {
|
||||||
|
return classNames(
|
||||||
|
props.baseClass,
|
||||||
|
props.customClass
|
||||||
|
)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -21,67 +21,65 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import $ui from '#build/ui'
|
import $ui from '#build/ui'
|
||||||
|
|
||||||
export default {
|
defineProps({
|
||||||
props: {
|
name: {
|
||||||
name: {
|
type: String,
|
||||||
type: String,
|
default: null
|
||||||
default: null
|
},
|
||||||
},
|
label: {
|
||||||
label: {
|
type: String,
|
||||||
type: String,
|
default: null
|
||||||
default: null
|
},
|
||||||
},
|
description: {
|
||||||
description: {
|
type: String,
|
||||||
type: String,
|
default: null
|
||||||
default: null
|
},
|
||||||
},
|
required: {
|
||||||
required: {
|
type: Boolean,
|
||||||
type: Boolean,
|
default: false
|
||||||
default: false
|
},
|
||||||
},
|
help: {
|
||||||
help: {
|
type: String,
|
||||||
type: String,
|
default: null
|
||||||
default: null
|
},
|
||||||
},
|
hint: {
|
||||||
hint: {
|
type: String,
|
||||||
type: String,
|
default: null
|
||||||
default: null
|
},
|
||||||
},
|
wrapperClass: {
|
||||||
wrapperClass: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.formGroup.wrapper
|
||||||
default: () => $ui.formGroup.wrapper
|
},
|
||||||
},
|
containerClass: {
|
||||||
containerClass: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.formGroup.container
|
||||||
default: () => $ui.formGroup.container
|
},
|
||||||
},
|
labelClass: {
|
||||||
labelClass: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.formGroup.label
|
||||||
default: () => $ui.formGroup.label
|
},
|
||||||
},
|
labelWrapperClass: {
|
||||||
labelWrapperClass: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.formGroup.labelWrapper
|
||||||
default: () => $ui.formGroup.labelWrapper
|
},
|
||||||
},
|
descriptionClass: {
|
||||||
descriptionClass: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.formGroup.description
|
||||||
default: () => $ui.formGroup.description
|
},
|
||||||
},
|
requiredClass: {
|
||||||
requiredClass: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.formGroup.required
|
||||||
default: () => $ui.formGroup.required
|
},
|
||||||
},
|
hintClass: {
|
||||||
hintClass: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.formGroup.hint
|
||||||
default: () => $ui.formGroup.hint
|
},
|
||||||
},
|
helpClass: {
|
||||||
helpClass: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.formGroup.help
|
||||||
default: () => $ui.formGroup.help
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -27,180 +27,164 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
|
import type { Ref } from 'vue'
|
||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
import Icon from '../elements/Icon'
|
import Icon from '../elements/Icon'
|
||||||
import { classNames } from '../../utils'
|
import { classNames } from '../../utils'
|
||||||
import $ui from '#build/ui'
|
import $ui from '#build/ui'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
components: {
|
modelValue: {
|
||||||
Icon
|
type: [String, Number],
|
||||||
|
default: ''
|
||||||
},
|
},
|
||||||
props: {
|
type: {
|
||||||
modelValue: {
|
type: String,
|
||||||
type: [String, Number],
|
default: 'text'
|
||||||
default: ''
|
},
|
||||||
},
|
name: {
|
||||||
type: {
|
type: String,
|
||||||
type: String,
|
required: true
|
||||||
default: 'text'
|
},
|
||||||
},
|
placeholder: {
|
||||||
name: {
|
type: String,
|
||||||
type: String,
|
default: null
|
||||||
required: true
|
},
|
||||||
},
|
required: {
|
||||||
placeholder: {
|
type: Boolean,
|
||||||
type: String,
|
default: false
|
||||||
default: null
|
},
|
||||||
},
|
disabled: {
|
||||||
required: {
|
type: Boolean,
|
||||||
type: Boolean,
|
default: false
|
||||||
default: false
|
},
|
||||||
},
|
readonly: {
|
||||||
disabled: {
|
type: Boolean,
|
||||||
type: Boolean,
|
default: false
|
||||||
default: false
|
},
|
||||||
},
|
autofocus: {
|
||||||
readonly: {
|
type: Boolean,
|
||||||
type: Boolean,
|
default: false
|
||||||
default: false
|
},
|
||||||
},
|
autocomplete: {
|
||||||
autofocus: {
|
type: String,
|
||||||
type: Boolean,
|
default: null
|
||||||
default: false
|
},
|
||||||
},
|
spellcheck: {
|
||||||
autocomplete: {
|
type: String,
|
||||||
type: String,
|
default: null
|
||||||
default: null
|
},
|
||||||
},
|
icon: {
|
||||||
spellcheck: {
|
type: String,
|
||||||
type: String,
|
default: null
|
||||||
default: null
|
},
|
||||||
},
|
loadingIcon: {
|
||||||
icon: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.input.icon.loading
|
||||||
default: null
|
},
|
||||||
},
|
trailing: {
|
||||||
loadingIcon: {
|
type: Boolean,
|
||||||
type: String,
|
default: false
|
||||||
default: () => $ui.input.icon.loading
|
},
|
||||||
},
|
leading: {
|
||||||
trailing: {
|
type: Boolean,
|
||||||
type: Boolean,
|
default: false
|
||||||
default: false
|
},
|
||||||
},
|
size: {
|
||||||
leading: {
|
type: String,
|
||||||
type: Boolean,
|
default: 'md',
|
||||||
default: false
|
validator (value: string) {
|
||||||
},
|
return Object.keys($ui.input.size).includes(value)
|
||||||
size: {
|
|
||||||
type: String,
|
|
||||||
default: 'md',
|
|
||||||
validator (value) {
|
|
||||||
return Object.keys($ui.input.size).includes(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
wrapperClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.input.wrapper
|
|
||||||
},
|
|
||||||
baseClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.input.base
|
|
||||||
},
|
|
||||||
iconBaseClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.input.icon.base
|
|
||||||
},
|
|
||||||
customClass: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
appearance: {
|
|
||||||
type: String,
|
|
||||||
default: 'default',
|
|
||||||
validator (value) {
|
|
||||||
return Object.keys($ui.input.appearance).includes(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
loading: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'focus', 'blur'],
|
wrapperClass: {
|
||||||
setup (props, { emit }) {
|
type: String,
|
||||||
const input = ref(null)
|
default: () => $ui.input.wrapper
|
||||||
|
},
|
||||||
const autoFocus = () => {
|
baseClass: {
|
||||||
if (props.autofocus) {
|
type: String,
|
||||||
input.value.focus()
|
default: () => $ui.input.base
|
||||||
}
|
},
|
||||||
|
iconBaseClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.input.icon.base
|
||||||
|
},
|
||||||
|
customClass: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
appearance: {
|
||||||
|
type: String,
|
||||||
|
default: 'default',
|
||||||
|
validator (value: string) {
|
||||||
|
return Object.keys($ui.input.appearance).includes(value)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const onInput = (value) => {
|
const emit = defineEmits(['update:modelValue', 'focus', 'blur'])
|
||||||
emit('update:modelValue', value)
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
const input: Ref<HTMLInputElement> = ref(null)
|
||||||
setTimeout(() => {
|
|
||||||
autoFocus()
|
|
||||||
}, 100)
|
|
||||||
})
|
|
||||||
|
|
||||||
const isLeading = computed(() => {
|
const autoFocus = () => {
|
||||||
return (props.icon && props.leading) || (props.icon && !props.trailing) || (props.loading && !props.trailing)
|
if (props.autofocus) {
|
||||||
})
|
input.value.focus()
|
||||||
|
|
||||||
const isTrailing = computed(() => {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
return props.icon
|
|
||||||
})
|
|
||||||
|
|
||||||
const iconClass = computed(() => {
|
|
||||||
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,
|
|
||||||
inputClass,
|
|
||||||
iconName,
|
|
||||||
iconClass,
|
|
||||||
iconLeadingWrapperClass,
|
|
||||||
iconTrailingWrapperClass,
|
|
||||||
isLeading,
|
|
||||||
isTrailing
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onInput = (value: string) => {
|
||||||
|
emit('update:modelValue', value)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
autoFocus()
|
||||||
|
}, 100)
|
||||||
|
})
|
||||||
|
|
||||||
|
const isLeading = computed(() => {
|
||||||
|
return (props.icon && props.leading) || (props.icon && !props.trailing) || (props.loading && !props.trailing)
|
||||||
|
})
|
||||||
|
|
||||||
|
const isTrailing = computed(() => {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
return props.icon
|
||||||
|
})
|
||||||
|
|
||||||
|
const iconClass = computed(() => {
|
||||||
|
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
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -26,88 +26,81 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { classNames } from '../../utils'
|
import { classNames } from '../../utils'
|
||||||
import $ui from '#build/ui'
|
import $ui from '#build/ui'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
props: {
|
value: {
|
||||||
value: {
|
type: [String, Number, Boolean],
|
||||||
type: [String, Number, Boolean],
|
default: null
|
||||||
default: null
|
|
||||||
},
|
|
||||||
modelValue: {
|
|
||||||
type: [String, Number, Boolean, Object],
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
disabled: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
help: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
required: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
wrapperClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.radio.wrapper
|
|
||||||
},
|
|
||||||
baseClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.radio.base
|
|
||||||
},
|
|
||||||
labelClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.radio.label
|
|
||||||
},
|
|
||||||
requiredClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.radio.required
|
|
||||||
},
|
|
||||||
helpClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.radio.help
|
|
||||||
},
|
|
||||||
customClass: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'focus', 'blur'],
|
modelValue: {
|
||||||
setup (props, { emit }) {
|
type: [String, Number, Boolean, Object],
|
||||||
const isChecked = computed({
|
default: null
|
||||||
get () {
|
},
|
||||||
return props.modelValue
|
name: {
|
||||||
},
|
type: String,
|
||||||
set (value) {
|
default: null
|
||||||
emit('update:modelValue', value)
|
},
|
||||||
}
|
disabled: {
|
||||||
})
|
type: Boolean,
|
||||||
|
default: false
|
||||||
const radioClass = computed(() => {
|
},
|
||||||
return classNames(
|
help: {
|
||||||
props.baseClass,
|
type: String,
|
||||||
props.customClass
|
default: null
|
||||||
)
|
},
|
||||||
})
|
label: {
|
||||||
|
type: String,
|
||||||
return {
|
default: null
|
||||||
isChecked,
|
},
|
||||||
radioClass
|
required: {
|
||||||
}
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
wrapperClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.radio.wrapper
|
||||||
|
},
|
||||||
|
baseClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.radio.base
|
||||||
|
},
|
||||||
|
labelClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.radio.label
|
||||||
|
},
|
||||||
|
requiredClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.radio.required
|
||||||
|
},
|
||||||
|
helpClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.radio.help
|
||||||
|
},
|
||||||
|
customClass: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue', 'focus', 'blur'])
|
||||||
|
|
||||||
|
const isChecked = computed({
|
||||||
|
get () {
|
||||||
|
return props.modelValue
|
||||||
|
},
|
||||||
|
set (value) {
|
||||||
|
emit('update:modelValue', value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const radioClass = computed(() => {
|
||||||
|
return classNames(
|
||||||
|
props.baseClass,
|
||||||
|
props.customClass
|
||||||
|
)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -42,172 +42,151 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { get } from 'lodash-es'
|
import { get } from 'lodash-es'
|
||||||
import Icon from '../elements/Icon'
|
import Icon from '../elements/Icon'
|
||||||
import { classNames } from '../../utils'
|
import { classNames } from '../../utils'
|
||||||
import $ui from '#build/ui'
|
import $ui from '#build/ui'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
components: {
|
modelValue: {
|
||||||
Icon
|
type: [String, Number, Object],
|
||||||
|
default: ''
|
||||||
},
|
},
|
||||||
props: {
|
name: {
|
||||||
modelValue: {
|
type: String,
|
||||||
type: [String, Number, Object],
|
required: true
|
||||||
default: ''
|
},
|
||||||
},
|
placeholder: {
|
||||||
name: {
|
type: String,
|
||||||
type: String,
|
default: null
|
||||||
required: true
|
},
|
||||||
},
|
required: {
|
||||||
placeholder: {
|
type: Boolean,
|
||||||
type: String,
|
default: false
|
||||||
default: null
|
},
|
||||||
},
|
disabled: {
|
||||||
required: {
|
type: Boolean,
|
||||||
type: Boolean,
|
default: false
|
||||||
default: false
|
},
|
||||||
},
|
options: {
|
||||||
disabled: {
|
type: Array,
|
||||||
type: Boolean,
|
default: () => []
|
||||||
default: false
|
},
|
||||||
},
|
size: {
|
||||||
options: {
|
type: String,
|
||||||
type: Array,
|
default: 'md',
|
||||||
default: () => []
|
validator (value: string) {
|
||||||
},
|
return Object.keys($ui.select.size).includes(value)
|
||||||
size: {
|
|
||||||
type: String,
|
|
||||||
default: 'md',
|
|
||||||
validator (value) {
|
|
||||||
return Object.keys($ui.select.size).includes(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
wrapperClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.select.wrapper
|
|
||||||
},
|
|
||||||
baseClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.select.base
|
|
||||||
},
|
|
||||||
iconBaseClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.select.icon.base
|
|
||||||
},
|
|
||||||
customClass: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
textAttribute: {
|
|
||||||
type: String,
|
|
||||||
default: 'text'
|
|
||||||
},
|
|
||||||
valueAttribute: {
|
|
||||||
type: String,
|
|
||||||
default: 'value'
|
|
||||||
},
|
|
||||||
icon: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue'],
|
wrapperClass: {
|
||||||
setup (props, { emit }) {
|
type: String,
|
||||||
const select = ref(null)
|
default: () => $ui.select.wrapper
|
||||||
|
},
|
||||||
|
baseClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.select.base
|
||||||
|
},
|
||||||
|
iconBaseClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.select.icon.base
|
||||||
|
},
|
||||||
|
customClass: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
textAttribute: {
|
||||||
|
type: String,
|
||||||
|
default: 'text'
|
||||||
|
},
|
||||||
|
valueAttribute: {
|
||||||
|
type: String,
|
||||||
|
default: 'value'
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const onInput = (value) => {
|
const emit = defineEmits(['update:modelValue'])
|
||||||
emit('update:modelValue', value)
|
|
||||||
}
|
|
||||||
|
|
||||||
const guessOptionValue = (option) => {
|
const onInput = (value: string) => {
|
||||||
return get(option, props.valueAttribute, get(option, props.textAttribute))
|
emit('update:modelValue', value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const guessOptionText = (option) => {
|
const guessOptionValue = (option: any) => {
|
||||||
return get(option, props.textAttribute, get(option, props.valueAttribute))
|
return get(option, props.valueAttribute, get(option, props.textAttribute))
|
||||||
}
|
}
|
||||||
|
|
||||||
const normalizeOption = (option) => {
|
const guessOptionText = (option: any) => {
|
||||||
if (['string', 'number', 'boolean'].includes(typeof option)) {
|
return get(option, props.textAttribute, get(option, props.valueAttribute))
|
||||||
return {
|
}
|
||||||
[props.valueAttribute]: option,
|
|
||||||
[props.textAttribute]: option
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...option,
|
|
||||||
[props.valueAttribute]: guessOptionValue(option),
|
|
||||||
[props.textAttribute]: guessOptionText(option)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const normalizedOptions = computed(() => {
|
|
||||||
return props.options.map(option => normalizeOption(option))
|
|
||||||
})
|
|
||||||
|
|
||||||
const normalizedOptionsWithPlaceholder = computed(() => {
|
|
||||||
if (!props.placeholder) {
|
|
||||||
return normalizedOptions.value
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
[props.valueAttribute]: '',
|
|
||||||
[props.textAttribute]: props.placeholder,
|
|
||||||
disabled: true
|
|
||||||
},
|
|
||||||
...normalizedOptions.value
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const normalizedValue = computed(() => {
|
|
||||||
const foundOption = normalizedOptionsWithPlaceholder.value.find(option => option.value === props.modelValue)
|
|
||||||
if (!foundOption) {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
|
|
||||||
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.wrapper
|
|
||||||
|
|
||||||
|
const normalizeOption = (option: any) => {
|
||||||
|
if (['string', 'number', 'boolean'].includes(typeof option)) {
|
||||||
return {
|
return {
|
||||||
select,
|
[props.valueAttribute]: option,
|
||||||
onInput,
|
[props.textAttribute]: option
|
||||||
guessOptionValue,
|
|
||||||
guessOptionText,
|
|
||||||
normalizeOption,
|
|
||||||
normalizedOptions,
|
|
||||||
normalizedOptionsWithPlaceholder,
|
|
||||||
normalizedValue,
|
|
||||||
selectClass,
|
|
||||||
iconClass,
|
|
||||||
iconWrapperClass
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...option,
|
||||||
|
[props.valueAttribute]: guessOptionValue(option),
|
||||||
|
[props.textAttribute]: guessOptionText(option)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const normalizedOptions = computed(() => {
|
||||||
|
return props.options.map(option => normalizeOption(option))
|
||||||
|
})
|
||||||
|
|
||||||
|
const normalizedOptionsWithPlaceholder = computed(() => {
|
||||||
|
if (!props.placeholder) {
|
||||||
|
return normalizedOptions.value
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
[props.valueAttribute]: '',
|
||||||
|
[props.textAttribute]: props.placeholder,
|
||||||
|
disabled: true
|
||||||
|
},
|
||||||
|
...normalizedOptions.value
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const normalizedValue = computed(() => {
|
||||||
|
const foundOption = normalizedOptionsWithPlaceholder.value.find(option => option.value === props.modelValue)
|
||||||
|
if (!foundOption) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
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.wrapper
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -18,145 +18,138 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
|
import type { Ref } from 'vue'
|
||||||
import { ref, computed, watch, onMounted, nextTick } from 'vue'
|
import { ref, computed, watch, onMounted, nextTick } from 'vue'
|
||||||
import { classNames } from '../../utils'
|
import { classNames } from '../../utils'
|
||||||
import $ui from '#build/ui'
|
import $ui from '#build/ui'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
props: {
|
modelValue: {
|
||||||
modelValue: {
|
type: [String, Number],
|
||||||
type: [String, Number],
|
default: ''
|
||||||
default: ''
|
},
|
||||||
},
|
name: {
|
||||||
name: {
|
type: String,
|
||||||
type: String,
|
required: true
|
||||||
required: true
|
},
|
||||||
},
|
placeholder: {
|
||||||
placeholder: {
|
type: String,
|
||||||
type: String,
|
default: null
|
||||||
default: null
|
},
|
||||||
},
|
required: {
|
||||||
required: {
|
type: Boolean,
|
||||||
type: Boolean,
|
default: false
|
||||||
default: false
|
},
|
||||||
},
|
disabled: {
|
||||||
disabled: {
|
type: Boolean,
|
||||||
type: Boolean,
|
default: false
|
||||||
default: false
|
},
|
||||||
},
|
rows: {
|
||||||
rows: {
|
type: Number,
|
||||||
type: Number,
|
default: 3
|
||||||
default: 3
|
},
|
||||||
},
|
autoresize: {
|
||||||
autoresize: {
|
type: Boolean,
|
||||||
type: Boolean,
|
default: false
|
||||||
default: false
|
},
|
||||||
},
|
autofocus: {
|
||||||
autofocus: {
|
type: Boolean,
|
||||||
type: Boolean,
|
default: false
|
||||||
default: false
|
},
|
||||||
},
|
autocomplete: {
|
||||||
autocomplete: {
|
type: String,
|
||||||
type: String,
|
default: null
|
||||||
default: null
|
},
|
||||||
},
|
appearance: {
|
||||||
appearance: {
|
type: String,
|
||||||
type: String,
|
default: 'default',
|
||||||
default: 'default',
|
validator (value: string) {
|
||||||
validator (value) {
|
return Object.keys($ui.textarea.appearance).includes(value)
|
||||||
return Object.keys($ui.textarea.appearance).includes(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
resize: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
type: String,
|
|
||||||
default: 'md',
|
|
||||||
validator (value) {
|
|
||||||
return Object.keys($ui.textarea.size).includes(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
wrapperClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.textarea.wrapper
|
|
||||||
},
|
|
||||||
baseClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.textarea.base
|
|
||||||
},
|
|
||||||
customClass: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'focus', 'blur'],
|
resize: {
|
||||||
setup (props, { emit }) {
|
type: Boolean,
|
||||||
const textarea = ref(null)
|
default: true
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: String,
|
||||||
|
default: 'md',
|
||||||
|
validator (value: string) {
|
||||||
|
return Object.keys($ui.textarea.size).includes(value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
wrapperClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.textarea.wrapper
|
||||||
|
},
|
||||||
|
baseClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.textarea.base
|
||||||
|
},
|
||||||
|
customClass: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const autoFocus = () => {
|
const emit = defineEmits(['update:modelValue', 'focus', 'blur'])
|
||||||
if (props.autofocus) {
|
|
||||||
textarea.value.focus()
|
const textarea: Ref<HTMLTextAreaElement> = ref(null)
|
||||||
}
|
|
||||||
|
const autoFocus = () => {
|
||||||
|
if (props.autofocus) {
|
||||||
|
textarea.value.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const autoResize = () => {
|
||||||
|
if (props.autoresize) {
|
||||||
|
if (!textarea.value) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const autoResize = () => {
|
textarea.value.rows = props.rows
|
||||||
if (props.autoresize) {
|
|
||||||
if (!textarea.value) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea.value.rows = props.rows
|
const styles = window.getComputedStyle(textarea.value)
|
||||||
|
const paddingTop = parseInt(styles.paddingTop)
|
||||||
|
const paddingBottom = parseInt(styles.paddingBottom)
|
||||||
|
const padding = paddingTop + paddingBottom
|
||||||
|
const lineHeight = parseInt(styles.lineHeight)
|
||||||
|
const { scrollHeight } = textarea.value
|
||||||
|
const newRows = (scrollHeight - padding) / lineHeight
|
||||||
|
|
||||||
const styles = window.getComputedStyle(textarea.value)
|
if (newRows > props.rows) {
|
||||||
const paddingTop = parseInt(styles.paddingTop)
|
textarea.value.rows = newRows
|
||||||
const paddingBottom = parseInt(styles.paddingBottom)
|
|
||||||
const padding = paddingTop + paddingBottom
|
|
||||||
const lineHeight = parseInt(styles.lineHeight)
|
|
||||||
const { scrollHeight } = textarea.value
|
|
||||||
const newRows = (scrollHeight - padding) / lineHeight
|
|
||||||
|
|
||||||
if (newRows > props.rows) {
|
|
||||||
textarea.value.rows = newRows
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const onInput = (value) => {
|
|
||||||
autoResize()
|
|
||||||
|
|
||||||
emit('update:modelValue', value)
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(() => props.modelValue, () => {
|
|
||||||
nextTick(autoResize)
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
autoFocus()
|
|
||||||
autoResize()
|
|
||||||
}, 100)
|
|
||||||
})
|
|
||||||
|
|
||||||
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,
|
|
||||||
textareaClass
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onInput = (value: string) => {
|
||||||
|
autoResize()
|
||||||
|
|
||||||
|
emit('update:modelValue', value)
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => props.modelValue, () => {
|
||||||
|
nextTick(autoResize)
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
autoFocus()
|
||||||
|
autoResize()
|
||||||
|
}, 100)
|
||||||
|
})
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -22,93 +22,86 @@
|
|||||||
</component>
|
</component>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { classNames } from '../../utils/'
|
import { classNames } from '../../utils/'
|
||||||
import $ui from '#build/ui'
|
import $ui from '#build/ui'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
props: {
|
padded: {
|
||||||
padded: {
|
type: Boolean,
|
||||||
type: Boolean,
|
default: false
|
||||||
default: false
|
},
|
||||||
},
|
rounded: {
|
||||||
rounded: {
|
type: Boolean,
|
||||||
type: Boolean,
|
default: true
|
||||||
default: true
|
},
|
||||||
},
|
baseClass: {
|
||||||
baseClass: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.card.base
|
||||||
default: () => $ui.card.base
|
},
|
||||||
},
|
backgroundClass: {
|
||||||
backgroundClass: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.card.background
|
||||||
default: () => $ui.card.background
|
},
|
||||||
},
|
borderColorClass: {
|
||||||
borderColorClass: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.card.border
|
||||||
default: () => $ui.card.border
|
},
|
||||||
},
|
shadowClass: {
|
||||||
shadowClass: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.card.shadow
|
||||||
default: () => $ui.card.shadow
|
},
|
||||||
},
|
ringClass: {
|
||||||
ringClass: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.card.ring
|
||||||
default: () => $ui.card.ring
|
},
|
||||||
},
|
roundedClass: {
|
||||||
roundedClass: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.card.rounded,
|
||||||
default: () => $ui.card.rounded,
|
validator (value: string) {
|
||||||
validator (value) {
|
return !value || ['sm', 'md', 'lg', 'xl', '2xl', '3xl'].map(size => `rounded-${size}`).includes(value)
|
||||||
return !value || ['sm', 'md', 'lg', 'xl', '2xl', '3xl'].map(size => `rounded-${size}`).includes(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
bodyClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.card.body
|
|
||||||
},
|
|
||||||
bodyBackgroundClass: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
headerClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.card.header
|
|
||||||
},
|
|
||||||
headerBackgroundClass: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
footerClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.card.footer
|
|
||||||
},
|
|
||||||
footerBackgroundClass: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
customClass: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props) {
|
bodyClass: {
|
||||||
const cardClass = computed(() => {
|
type: String,
|
||||||
return classNames(
|
default: () => $ui.card.body
|
||||||
props.baseClass,
|
},
|
||||||
props.padded && props.rounded && props.roundedClass,
|
bodyBackgroundClass: {
|
||||||
!props.padded && props.rounded && `sm:${props.roundedClass}`,
|
type: String,
|
||||||
props.ringClass,
|
default: null
|
||||||
props.shadowClass,
|
},
|
||||||
props.backgroundClass,
|
headerClass: {
|
||||||
props.customClass
|
type: String,
|
||||||
)
|
default: () => $ui.card.header
|
||||||
})
|
},
|
||||||
|
headerBackgroundClass: {
|
||||||
return {
|
type: String,
|
||||||
cardClass
|
default: null
|
||||||
}
|
},
|
||||||
|
footerClass: {
|
||||||
|
type: String,
|
||||||
|
default: () => $ui.card.footer
|
||||||
|
},
|
||||||
|
footerBackgroundClass: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
customClass: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
|
const cardClass = computed(() => {
|
||||||
|
return classNames(
|
||||||
|
props.baseClass,
|
||||||
|
props.padded && props.rounded && props.roundedClass,
|
||||||
|
!props.padded && props.rounded && `sm:${props.roundedClass}`,
|
||||||
|
props.ringClass,
|
||||||
|
props.shadowClass,
|
||||||
|
props.backgroundClass,
|
||||||
|
props.customClass
|
||||||
|
)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -30,69 +30,63 @@
|
|||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import Icon from '../elements/Icon'
|
import Icon from '../elements/Icon'
|
||||||
import Link from '../elements/Link'
|
import Link from '../elements/Link'
|
||||||
import $ui from '#build/ui'
|
import $ui from '#build/ui'
|
||||||
|
|
||||||
export default {
|
defineProps({
|
||||||
components: {
|
links: {
|
||||||
Icon,
|
type: Array,
|
||||||
Link
|
required: true
|
||||||
},
|
},
|
||||||
props: {
|
wrapperClass: {
|
||||||
links: {
|
type: String,
|
||||||
type: Array,
|
default: () => $ui.verticalNavigation.wrapper
|
||||||
required: true
|
},
|
||||||
},
|
baseClass: {
|
||||||
wrapperClass: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.verticalNavigation.base
|
||||||
default: () => $ui.verticalNavigation.wrapper
|
},
|
||||||
},
|
spacingClass: {
|
||||||
baseClass: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.verticalNavigation.spacing
|
||||||
default: () => $ui.verticalNavigation.base
|
},
|
||||||
},
|
activeClass: {
|
||||||
spacingClass: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.verticalNavigation.active
|
||||||
default: () => $ui.verticalNavigation.spacing
|
},
|
||||||
},
|
inactiveClass: {
|
||||||
activeClass: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.verticalNavigation.inactive
|
||||||
default: () => $ui.verticalNavigation.active
|
},
|
||||||
},
|
iconBaseClass: {
|
||||||
inactiveClass: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.verticalNavigation.icon.base
|
||||||
default: () => $ui.verticalNavigation.inactive
|
},
|
||||||
},
|
iconSpacingClass: {
|
||||||
iconBaseClass: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.verticalNavigation.icon.spacing
|
||||||
default: () => $ui.verticalNavigation.icon.base
|
},
|
||||||
},
|
iconActiveClass: {
|
||||||
iconSpacingClass: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.verticalNavigation.icon.active
|
||||||
default: () => $ui.verticalNavigation.icon.spacing
|
},
|
||||||
},
|
iconInactiveClass: {
|
||||||
iconActiveClass: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.verticalNavigation.icon.inactive
|
||||||
default: () => $ui.verticalNavigation.icon.active
|
},
|
||||||
},
|
badgeBaseClass: {
|
||||||
iconInactiveClass: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.verticalNavigation.badge.base
|
||||||
default: () => $ui.verticalNavigation.icon.inactive
|
},
|
||||||
},
|
badgeActiveClass: {
|
||||||
badgeBaseClass: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.verticalNavigation.badge.active
|
||||||
default: () => $ui.verticalNavigation.badge.base
|
},
|
||||||
},
|
badgeInactiveClass: {
|
||||||
badgeActiveClass: {
|
type: String,
|
||||||
type: String,
|
default: () => $ui.verticalNavigation.badge.inactive
|
||||||
default: () => $ui.verticalNavigation.badge.active
|
|
||||||
},
|
|
||||||
badgeInactiveClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.verticalNavigation.badge.inactive
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -53,89 +53,81 @@
|
|||||||
</TransitionRoot>
|
</TransitionRoot>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { Dialog, DialogPanel, TransitionRoot, TransitionChild } from '@headlessui/vue'
|
import { Dialog, DialogPanel, TransitionRoot, TransitionChild } from '@headlessui/vue'
|
||||||
import { classNames } from '../../utils/'
|
import { classNames } from '../../utils/'
|
||||||
import Card from '../layout/Card'
|
import Card from '../layout/Card'
|
||||||
import $ui from '#build/ui'
|
import $ui from '#build/ui'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
components: {
|
modelValue: {
|
||||||
Dialog,
|
type: Boolean,
|
||||||
DialogPanel,
|
default: false
|
||||||
TransitionRoot,
|
|
||||||
TransitionChild,
|
|
||||||
Card
|
|
||||||
},
|
},
|
||||||
inheritAttrs: false,
|
appear: {
|
||||||
props: {
|
type: Boolean,
|
||||||
modelValue: {
|
default: false
|
||||||
type: Boolean,
|
},
|
||||||
default: false
|
baseClass: {
|
||||||
},
|
type: String,
|
||||||
appear: {
|
default: () => $ui.modal.base
|
||||||
type: Boolean,
|
},
|
||||||
default: false
|
backgroundClass: {
|
||||||
},
|
type: String,
|
||||||
baseClass: {
|
default: () => $ui.modal.background
|
||||||
type: String,
|
},
|
||||||
default: () => $ui.modal.base
|
shadowClass: {
|
||||||
},
|
type: String,
|
||||||
backgroundClass: {
|
default: () => $ui.modal.shadow
|
||||||
type: String,
|
},
|
||||||
default: () => $ui.modal.background
|
ringClass: {
|
||||||
},
|
type: String,
|
||||||
shadowClass: {
|
default: () => $ui.modal.ring
|
||||||
type: String,
|
},
|
||||||
default: () => $ui.modal.shadow
|
roundedClass: {
|
||||||
},
|
type: String,
|
||||||
ringClass: {
|
default: () => $ui.modal.rounded
|
||||||
type: String,
|
},
|
||||||
default: () => $ui.modal.ring
|
widthClass: {
|
||||||
},
|
type: String,
|
||||||
roundedClass: {
|
default: () => $ui.modal.width,
|
||||||
type: String,
|
validator (value: string) {
|
||||||
default: () => $ui.modal.rounded
|
return ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', '4xl', '5xl', '6xl', '7xl'].map(width => `max-w-${width}`).includes(value)
|
||||||
},
|
|
||||||
widthClass: {
|
|
||||||
type: String,
|
|
||||||
default: () => $ui.modal.width,
|
|
||||||
validator (value) {
|
|
||||||
return ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', '4xl', '5xl', '6xl', '7xl'].map(width => `max-w-${width}`).includes(value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
emits: ['update:modelValue', 'close'],
|
})
|
||||||
setup (props, { emit }) {
|
|
||||||
const isOpen = computed({
|
|
||||||
get () {
|
|
||||||
return props.modelValue
|
|
||||||
},
|
|
||||||
set (value) {
|
|
||||||
emit('update:modelValue', value)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const modalClass = computed(() => {
|
const emit = defineEmits(['update:modelValue', 'close'])
|
||||||
return classNames(
|
|
||||||
props.baseClass,
|
const isOpen = computed({
|
||||||
|
get () {
|
||||||
|
return props.modelValue
|
||||||
|
},
|
||||||
|
set (value) {
|
||||||
|
emit('update:modelValue', value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const modalClass = computed(() => {
|
||||||
|
return classNames(
|
||||||
|
props.baseClass,
|
||||||
`sm:${props.widthClass}`,
|
`sm:${props.widthClass}`,
|
||||||
props.backgroundClass,
|
props.backgroundClass,
|
||||||
props.shadowClass,
|
props.shadowClass,
|
||||||
props.ringClass,
|
props.ringClass,
|
||||||
props.roundedClass
|
props.roundedClass
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
function close (value: boolean) {
|
||||||
isOpen,
|
isOpen.value = value
|
||||||
modalClass,
|
emit('close')
|
||||||
close (value) {
|
}
|
||||||
isOpen.value = value
|
</script>
|
||||||
emit('close')
|
|
||||||
}
|
<script lang="ts">
|
||||||
}
|
export default {
|
||||||
}
|
inheritAttrs: false
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -55,148 +55,131 @@
|
|||||||
</transition>
|
</transition>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted, onUnmounted, watchEffect } from 'vue'
|
import { ref, computed, onMounted, onUnmounted, watchEffect } from 'vue'
|
||||||
|
|
||||||
import Icon from '../elements/Icon'
|
import Icon from '../elements/Icon'
|
||||||
import Button from '../elements/Button'
|
import Button from '../elements/Button'
|
||||||
import { useTimer } from '../../composables/useTimer'
|
import { useTimer } from '../../composables/useTimer'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
components: {
|
id: {
|
||||||
Icon,
|
type: String,
|
||||||
Button
|
required: true
|
||||||
},
|
},
|
||||||
props: {
|
type: {
|
||||||
id: {
|
type: String,
|
||||||
type: String,
|
required: true,
|
||||||
required: true
|
default: 'info',
|
||||||
},
|
validator (value: string) {
|
||||||
type: {
|
return ['info', 'success', 'error', 'warning'].includes(value)
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
default: 'info',
|
|
||||||
validator (value) {
|
|
||||||
return ['info', 'success', 'error', 'warning'].includes(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
icon: {
|
|
||||||
type: String,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
timeout: {
|
|
||||||
type: Number,
|
|
||||||
default: 5000
|
|
||||||
},
|
|
||||||
undo: {
|
|
||||||
type: Function,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
callback: {
|
|
||||||
type: Function,
|
|
||||||
default: null
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['close'],
|
title: {
|
||||||
setup (props, { emit }) {
|
type: String,
|
||||||
let timer = null
|
required: true
|
||||||
const remaining = ref(props.timeout)
|
},
|
||||||
|
description: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
timeout: {
|
||||||
|
type: Number,
|
||||||
|
default: 5000
|
||||||
|
},
|
||||||
|
undo: {
|
||||||
|
type: Function,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
callback: {
|
||||||
|
type: Function,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const iconName = computed(() => {
|
const emit = defineEmits(['close'])
|
||||||
return props.icon || ({
|
|
||||||
warning: 'heroicons-outline:exclamation-circle',
|
|
||||||
info: 'heroicons-outline:information-circle',
|
|
||||||
success: 'heroicons-outline:check-circle',
|
|
||||||
error: 'heroicons-outline:x-circle'
|
|
||||||
})[props.type]
|
|
||||||
})
|
|
||||||
|
|
||||||
const iconClass = computed(() => {
|
let timer: any = null
|
||||||
return ({
|
const remaining = ref(props.timeout)
|
||||||
warning: 'text-orange-400',
|
|
||||||
info: 'text-blue-400',
|
|
||||||
success: 'text-green-400',
|
|
||||||
error: 'text-red-400'
|
|
||||||
})[props.type] || 'u-text-gray-400'
|
|
||||||
})
|
|
||||||
|
|
||||||
const progressBarStyle = computed(() => {
|
const iconName = computed(() => {
|
||||||
const remainingPercent = remaining.value / props.timeout * 100
|
return props.icon || ({
|
||||||
return { width: `${remainingPercent || 0}%` }
|
warning: 'heroicons-outline:exclamation-circle',
|
||||||
})
|
info: 'heroicons-outline:information-circle',
|
||||||
|
success: 'heroicons-outline:check-circle',
|
||||||
|
error: 'heroicons-outline:x-circle'
|
||||||
|
})[props.type]
|
||||||
|
})
|
||||||
|
|
||||||
function onMouseover () {
|
const iconClass = computed(() => {
|
||||||
if (timer) {
|
return ({
|
||||||
timer.pause()
|
warning: 'text-orange-400',
|
||||||
}
|
info: 'text-blue-400',
|
||||||
}
|
success: 'text-green-400',
|
||||||
|
error: 'text-red-400'
|
||||||
|
})[props.type] || 'u-text-gray-400'
|
||||||
|
})
|
||||||
|
|
||||||
function onMouseleave () {
|
const progressBarStyle = computed(() => {
|
||||||
if (timer) {
|
const remainingPercent = remaining.value / props.timeout * 100
|
||||||
timer.resume()
|
return { width: `${remainingPercent || 0}%` }
|
||||||
}
|
})
|
||||||
}
|
|
||||||
|
|
||||||
function onClose () {
|
function onMouseover () {
|
||||||
if (timer) {
|
if (timer) {
|
||||||
timer.stop()
|
timer.pause()
|
||||||
}
|
|
||||||
|
|
||||||
if (props.callback) {
|
|
||||||
props.callback()
|
|
||||||
}
|
|
||||||
|
|
||||||
emit('close')
|
|
||||||
}
|
|
||||||
|
|
||||||
function onUndo () {
|
|
||||||
if (timer) {
|
|
||||||
timer.stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.undo) {
|
|
||||||
props.undo()
|
|
||||||
}
|
|
||||||
|
|
||||||
emit('close')
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
if (!props.timeout) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
timer = useTimer(() => {
|
|
||||||
onClose()
|
|
||||||
}, props.timeout)
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
remaining.value = timer.remaining.value
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
timer.stop()
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
timer,
|
|
||||||
iconName,
|
|
||||||
iconClass,
|
|
||||||
progressBarStyle,
|
|
||||||
onMouseover,
|
|
||||||
onMouseleave,
|
|
||||||
onClose,
|
|
||||||
onUndo
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onMouseleave () {
|
||||||
|
if (timer) {
|
||||||
|
timer.resume()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onClose () {
|
||||||
|
if (timer) {
|
||||||
|
timer.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.callback) {
|
||||||
|
props.callback()
|
||||||
|
}
|
||||||
|
|
||||||
|
emit('close')
|
||||||
|
}
|
||||||
|
|
||||||
|
function onUndo () {
|
||||||
|
if (timer) {
|
||||||
|
timer.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.undo) {
|
||||||
|
props.undo()
|
||||||
|
}
|
||||||
|
|
||||||
|
emit('close')
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!props.timeout) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
timer = useTimer(() => {
|
||||||
|
onClose()
|
||||||
|
}, props.timeout)
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
remaining.value = timer.remaining.value
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
timer.stop()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -24,135 +24,121 @@
|
|||||||
</Popover>
|
</Popover>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
|
import type { Ref } from 'vue'
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'
|
import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'
|
||||||
|
|
||||||
import { usePopper } from '../../utils'
|
import { usePopper } from '../../utils'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
components: {
|
placement: {
|
||||||
Popover,
|
type: String,
|
||||||
PopoverButton,
|
default: 'bottom'
|
||||||
PopoverPanel
|
|
||||||
},
|
},
|
||||||
props: {
|
strategy: {
|
||||||
placement: {
|
type: String,
|
||||||
type: String,
|
default: 'fixed'
|
||||||
default: 'bottom'
|
},
|
||||||
},
|
mode: {
|
||||||
strategy: {
|
type: String,
|
||||||
type: String,
|
default: 'click',
|
||||||
default: 'fixed'
|
validator: (value: string) => {
|
||||||
},
|
return ['click', 'hover'].includes(value)
|
||||||
mode: {
|
|
||||||
type: String,
|
|
||||||
default: 'click',
|
|
||||||
validator: (value) => {
|
|
||||||
return ['click', 'hover'].includes(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
wrapperClass: {
|
|
||||||
type: String,
|
|
||||||
default: 'relative'
|
|
||||||
},
|
|
||||||
containerClass: {
|
|
||||||
type: String,
|
|
||||||
default: 'z-10 py-2'
|
|
||||||
},
|
|
||||||
panelClass: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props) {
|
wrapperClass: {
|
||||||
const [trigger, container] = usePopper({
|
type: String,
|
||||||
placement: props.placement,
|
default: 'relative'
|
||||||
strategy: props.strategy,
|
},
|
||||||
modifiers: [{
|
containerClass: {
|
||||||
name: 'offset',
|
type: String,
|
||||||
options: {
|
default: 'z-10 py-2'
|
||||||
offset: 0
|
},
|
||||||
}
|
panelClass: {
|
||||||
},
|
type: String,
|
||||||
{
|
default: ''
|
||||||
name: 'computeStyles',
|
|
||||||
options: {
|
|
||||||
gpuAcceleration: false,
|
|
||||||
adaptive: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'preventOverflow',
|
|
||||||
options: {
|
|
||||||
padding: 8
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
})
|
|
||||||
|
|
||||||
const popoverApi = ref(null)
|
|
||||||
|
|
||||||
let openTimeout = null
|
|
||||||
let closeTimeout = null
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
const popoverProvides = trigger.value?.$.provides
|
|
||||||
const popoverProvidesSymbols = Object.getOwnPropertySymbols(popoverProvides)
|
|
||||||
popoverApi.value = popoverProvidesSymbols.length && popoverProvides[popoverProvidesSymbols[0]]
|
|
||||||
// stop trigger click propagation on hover
|
|
||||||
popoverApi.value.button.addEventListener('click', (e) => {
|
|
||||||
if (props.mode === 'hover') {
|
|
||||||
e.stopPropagation()
|
|
||||||
}
|
|
||||||
}, true)
|
|
||||||
}, 0)
|
|
||||||
})
|
|
||||||
|
|
||||||
function onMouseOver () {
|
|
||||||
if (props.mode !== 'hover' || !popoverApi.value) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// cancel programmed closing
|
|
||||||
if (closeTimeout) {
|
|
||||||
clearTimeout(closeTimeout)
|
|
||||||
closeTimeout = null
|
|
||||||
}
|
|
||||||
// dropdown already open
|
|
||||||
if (popoverApi.value.popoverState === 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
openTimeout = openTimeout || setTimeout(() => {
|
|
||||||
popoverApi.value.togglePopover && popoverApi.value.togglePopover()
|
|
||||||
openTimeout = null
|
|
||||||
}, 50)
|
|
||||||
}
|
|
||||||
|
|
||||||
function onMouseLeave () {
|
|
||||||
if (props.mode !== 'hover' || !popoverApi.value) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// cancel programmed opening
|
|
||||||
if (openTimeout) {
|
|
||||||
clearTimeout(openTimeout)
|
|
||||||
openTimeout = null
|
|
||||||
}
|
|
||||||
// dropdown already closed
|
|
||||||
if (popoverApi.value.popoverState === 1) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
closeTimeout = closeTimeout || setTimeout(() => {
|
|
||||||
popoverApi.value.closePopover && popoverApi.value.closePopover()
|
|
||||||
closeTimeout = null
|
|
||||||
}, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
trigger,
|
|
||||||
container,
|
|
||||||
onMouseOver,
|
|
||||||
onMouseLeave
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const [trigger, container] = usePopper({
|
||||||
|
placement: props.placement,
|
||||||
|
strategy: props.strategy,
|
||||||
|
modifiers: [{
|
||||||
|
name: 'offset',
|
||||||
|
options: {
|
||||||
|
offset: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'computeStyles',
|
||||||
|
options: {
|
||||||
|
gpuAcceleration: false,
|
||||||
|
adaptive: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'preventOverflow',
|
||||||
|
options: {
|
||||||
|
padding: 8
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
|
||||||
|
const popoverApi: Ref<any> = ref(null)
|
||||||
|
|
||||||
|
let openTimeout: NodeJS.Timeout | null = null
|
||||||
|
let closeTimeout: NodeJS.Timeout | null = null
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
const popoverProvides = trigger.value?.$.provides
|
||||||
|
const popoverProvidesSymbols = Object.getOwnPropertySymbols(popoverProvides)
|
||||||
|
popoverApi.value = popoverProvidesSymbols.length && popoverProvides[popoverProvidesSymbols[0]]
|
||||||
|
// stop trigger click propagation on hover
|
||||||
|
popoverApi.value.button.addEventListener('click', (e: Event) => {
|
||||||
|
if (props.mode === 'hover') {
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
}, true)
|
||||||
|
}, 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
function onMouseOver () {
|
||||||
|
if (props.mode !== 'hover' || !popoverApi.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// cancel programmed closing
|
||||||
|
if (closeTimeout) {
|
||||||
|
clearTimeout(closeTimeout)
|
||||||
|
closeTimeout = null
|
||||||
|
}
|
||||||
|
// dropdown already open
|
||||||
|
if (popoverApi.value.popoverState === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
openTimeout = openTimeout || setTimeout(() => {
|
||||||
|
popoverApi.value.togglePopover && popoverApi.value.togglePopover()
|
||||||
|
openTimeout = null
|
||||||
|
}, 50)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMouseLeave () {
|
||||||
|
if (props.mode !== 'hover' || !popoverApi.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// cancel programmed opening
|
||||||
|
if (openTimeout) {
|
||||||
|
clearTimeout(openTimeout)
|
||||||
|
openTimeout = null
|
||||||
|
}
|
||||||
|
// dropdown already closed
|
||||||
|
if (popoverApi.value.popoverState === 1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
closeTimeout = closeTimeout || setTimeout(() => {
|
||||||
|
popoverApi.value.closePopover && popoverApi.value.closePopover()
|
||||||
|
closeTimeout = null
|
||||||
|
}, 0)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -24,74 +24,65 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { usePopper } from '../../utils'
|
import { usePopper } from '../../utils'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
props: {
|
text: {
|
||||||
text: {
|
type: String,
|
||||||
type: String,
|
default: null
|
||||||
default: null
|
},
|
||||||
},
|
placement: {
|
||||||
placement: {
|
type: String,
|
||||||
type: String,
|
default: 'bottom',
|
||||||
default: 'bottom',
|
validator: (value: string) => {
|
||||||
validator: (value) => {
|
return ['auto', 'auto-start', 'auto-end', 'top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'right', 'right-start', 'right-end', 'left', 'left-start', 'left-end'].includes(value)
|
||||||
return ['auto', 'auto-start', 'auto-end', 'top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'right', 'right-start', 'right-end', 'left', 'left-start', 'left-end'].includes(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
strategy: {
|
|
||||||
type: String,
|
|
||||||
default: 'fixed',
|
|
||||||
validator: (value) => {
|
|
||||||
return ['absolute', 'fixed'].includes(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
wrapperClass: {
|
|
||||||
type: String,
|
|
||||||
default: 'relative inline-flex'
|
|
||||||
},
|
|
||||||
containerClass: {
|
|
||||||
type: String,
|
|
||||||
default: 'z-10 py-2'
|
|
||||||
},
|
|
||||||
tooltipClass: {
|
|
||||||
type: String,
|
|
||||||
default: 'flex items-center justify-center invisible w-auto h-6 max-w-xs px-2 space-x-1 truncate rounded shadow lg:visible u-bg-gray-800 truncate u-text-gray-50 text-xs'
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props) {
|
strategy: {
|
||||||
const open = ref(false)
|
type: String,
|
||||||
const [trigger, container] = usePopper({
|
default: 'fixed',
|
||||||
placement: props.placement,
|
validator: (value: string) => {
|
||||||
strategy: props.strategy,
|
return ['absolute', 'fixed'].includes(value)
|
||||||
modifiers: [{
|
|
||||||
name: 'offset',
|
|
||||||
options: {
|
|
||||||
offset: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'computeStyles',
|
|
||||||
options: {
|
|
||||||
gpuAcceleration: false,
|
|
||||||
adaptive: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'preventOverflow',
|
|
||||||
options: {
|
|
||||||
padding: 8
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
open,
|
|
||||||
trigger,
|
|
||||||
container
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
wrapperClass: {
|
||||||
|
type: String,
|
||||||
|
default: 'relative inline-flex'
|
||||||
|
},
|
||||||
|
containerClass: {
|
||||||
|
type: String,
|
||||||
|
default: 'z-10 py-2'
|
||||||
|
},
|
||||||
|
tooltipClass: {
|
||||||
|
type: String,
|
||||||
|
default: 'flex items-center justify-center invisible w-auto h-6 max-w-xs px-2 space-x-1 truncate rounded shadow lg:visible u-bg-gray-800 truncate u-text-gray-50 text-xs'
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
|
const open = ref(false)
|
||||||
|
const [trigger, container] = usePopper({
|
||||||
|
placement: props.placement,
|
||||||
|
strategy: props.strategy,
|
||||||
|
modifiers: [{
|
||||||
|
name: 'offset',
|
||||||
|
options: {
|
||||||
|
offset: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'computeStyles',
|
||||||
|
options: {
|
||||||
|
gpuAcceleration: false,
|
||||||
|
adaptive: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'preventOverflow',
|
||||||
|
options: {
|
||||||
|
padding: 8
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user