80 lines
1.7 KiB
Vue
80 lines
1.7 KiB
Vue
<script setup lang="ts">
|
|
import { ref, computed, useSlots, onMounted } from 'vue'
|
|
import { type ColorProps, useColor } from '~/composables/colors'
|
|
|
|
import Loader from '~/components/ui/Loader.vue'
|
|
|
|
interface Props {
|
|
variant?: 'solid' | 'outline' | 'ghost'
|
|
width?: 'standard' | 'auto' | 'full'
|
|
alignText?: 'left' | 'center' | 'right'
|
|
|
|
isActive?: boolean
|
|
isLoading?: boolean
|
|
|
|
shadow?: boolean
|
|
round?: boolean
|
|
icon?: string
|
|
|
|
onClick?: (...args: any[]) => void | Promise<void>
|
|
|
|
autofocus? : boolean
|
|
}
|
|
|
|
const props = defineProps<Props & ColorProps>()
|
|
|
|
const color = useColor(() => props.color)
|
|
|
|
const slots = useSlots()
|
|
const iconOnly = computed(() => !!props.icon && !slots.default)
|
|
|
|
const internalLoader = ref(false)
|
|
const isLoading = computed(() => props.isLoading || internalLoader.value)
|
|
|
|
const button = ref()
|
|
|
|
console.log("props.autofocus", props.autofocus)
|
|
|
|
const click = async (...args: any[]) => {
|
|
internalLoader.value = true
|
|
|
|
try {
|
|
await props.onClick?.(...args)
|
|
} finally {
|
|
internalLoader.value = false
|
|
}
|
|
}
|
|
onMounted(() => {
|
|
if (props.autofocus) button.value.focus();
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<button ref="button" class="funkwhale is-colored button" :class="[
|
|
color,
|
|
'is-' + (variant ?? 'solid'),
|
|
'is-' + (width ?? 'standard'),
|
|
'is-aligned-' + (alignText ?? 'center'),
|
|
{
|
|
'is-active': isActive,
|
|
'is-loading': isLoading,
|
|
'icon-only': iconOnly,
|
|
'has-icon': !!icon,
|
|
'is-round': round,
|
|
'is-shadow': shadow
|
|
}
|
|
]" @click="click">
|
|
<i v-if="icon" :class="['bi', icon]" />
|
|
|
|
<span>
|
|
<slot />
|
|
</span>
|
|
|
|
<Loader v-if="isLoading" :container="false" />
|
|
</button>
|
|
</template>
|
|
|
|
<style lang="scss">
|
|
@import './button.scss'
|
|
</style>
|