feat: Added href prop to button component

This commit is contained in:
Ilya Artamonov
2023-09-19 20:02:53 +03:00
parent e749fd726c
commit 0a792fc0d5
4 changed files with 67 additions and 53 deletions

View File

@@ -13,6 +13,7 @@ import ButtonIconExample from './button/examples/ButtonIconExample.vue';
import ButtonSquareExample from './button/examples/ButtonSquareExample.vue';
import ButtonDisabledExample from './button/examples/ButtonDisabledExample.vue';
import ButtonLoadingExample from './button/examples/ButtonLoadingExample.vue';
import ButtonLinkExample from './button/examples/ButtonLinkExample.vue';
</script>
# Vue Button - Flowbite
@@ -372,7 +373,20 @@ import { Button } from 'flowbite-vue'
</Button>
</template>
```
## Prop - href
You can add a link to a `Button` component.
Additionally you can add `tag` prop to change button component to `router-link`
<ButtonLinkExample />
```vue
<script setup>
import { Button } from 'flowbite-vue'
</script>
<template>
<Button color="default" href="https://google.com" target="_blank">Default</Button>
<Button href="/about" tag="router-link">Router link</Button>
</template>
```
## Slot - default

View File

@@ -0,0 +1,8 @@
<template>
<div class="vp-raw inline-flex align-center gap-2 flex-wrap">
<Button color="default" href="https://google.com" target="_blank">Default</Button>
</div>
</template>
<script setup>
import { Button } from '../../../../src/index'
</script>

View File

@@ -1,5 +1,5 @@
<template>
<button :class="wrapperClasses">
<component :is="buttonComponent" :class="wrapperClasses" :[linkAttr]="href">
<div v-if="!isOutlineGradient && ($slots.prefix || loadingPrefix)" class="mr-2">
<!--automatically add mr class if slot provided or loading -->
<spinner :color="spinnerColor" :size="spinnerSize" v-if="loadingPrefix" />
@@ -7,19 +7,19 @@
</div>
<span :class="spanClasses">
<div v-if="isOutlineGradient && ($slots.prefix || loadingPrefix)" class="mr-2">
<span v-if="isOutlineGradient && ($slots.prefix || loadingPrefix)" class="mr-2">
<!--if outline gradient - need to place slots inside span -->
<spinner :color="spinnerColor" :size="spinnerSize" v-if="loadingPrefix" />
<slot name="prefix" v-else />
</div>
</span>
<slot />
<div v-if="isOutlineGradient && ($slots.suffix || loadingSuffix)" class="ml-2">
<span v-if="isOutlineGradient && ($slots.suffix || loadingSuffix)" class="ml-2">
<!--if outline gradient - need to place slots inside span -->
<spinner :color="spinnerColor" :size="spinnerSize" v-if="loadingSuffix" />
<slot name="suffix" v-else />
</div>
</span>
</span>
<div v-if="!isOutlineGradient && ($slots.suffix || loadingSuffix)" class="ml-2">
@@ -27,55 +27,42 @@
<spinner :color="spinnerColor" :size="spinnerSize" v-if="loadingSuffix" />
<slot name="suffix" v-else />
</div>
</button>
</component>
</template>
<script lang="ts" setup>
import { computed, toRefs, type PropType } from 'vue'
import { computed, resolveComponent, toRefs } from 'vue'
import Spinner from '../Spinner/Spinner.vue'
import { useButtonClasses } from './composables/useButtonClasses'
import { useButtonSpinner } from './composables/useButtonSpinner'
import type { ButtonGradient, ButtonMonochromeGradient, ButtonSize, ButtonVariant } from './types'
const props = defineProps({
color: {
type: String as PropType<ButtonVariant>,
default: 'default',
},
gradient: {
type: [String, null] as PropType<ButtonGradient | null>,
default: null,
},
size: {
type: String as PropType<ButtonSize>,
default: 'md',
},
shadow: {
type: [String, null] as PropType<ButtonMonochromeGradient | '' | null>,
default: null,
},
pill: {
type: Boolean,
default: false,
},
square: {
type: Boolean,
default: false,
},
outline: {
type: Boolean,
default: false,
},
loading: {
type: Boolean,
default: false,
},
loadingPosition: {
type: String as PropType<'suffix' | 'prefix'>,
default: 'prefix',
},
disabled: {
type: Boolean,
default: false,
},
interface IButtonProps {
color?: ButtonVariant
gradient?: ButtonGradient | null
size?: ButtonSize
shadow?: ButtonMonochromeGradient | null
pill?: boolean
square?: boolean
outline?: boolean
loading?: boolean
loadingPosition?: 'suffix' | 'prefix'
disabled?: boolean
href?: string
tag?: string
}
const props = withDefaults(defineProps<IButtonProps>(), {
color: 'default',
gradient: null,
size: 'md',
shadow: null,
pill: false,
square: false,
outline: false,
loading: false,
loadingPosition: 'prefix',
disabled: false,
href: '',
tag: 'a',
})
const isOutlineGradient = computed(() => props.outline && props.gradient)
@@ -85,4 +72,8 @@ const loadingSuffix = computed(() => props.loading && props.loadingPosition ===
const { wrapperClasses, spanClasses } = useButtonClasses(toRefs(props))
const { color: spinnerColor, size: spinnerSize } = useButtonSpinner(toRefs(props))
const linkComponent = props.tag !== 'a' ? resolveComponent(props.tag) : 'a'
const buttonComponent = props.href ? linkComponent : 'button'
const linkAttr = props.tag === 'router-link' || props.tag === 'nuxt-link' ? 'to' : 'href'
</script>

View File

@@ -2,6 +2,7 @@ import type { Ref } from 'vue'
import { computed, useSlots } from 'vue'
import classNames from 'classnames'
import type { ButtonDuotoneGradient, ButtonGradient, ButtonMonochromeGradient, ButtonSize, ButtonVariant } from '../types'
import { twMerge } from 'tailwind-merge'
export type ButtonClassMap<T extends string> = { hover: Record<T, string>; default: Record<T, string> }
@@ -231,21 +232,21 @@ export function useButtonClasses(props: UseButtonClassesProps): { wrapperClasses
}
}
return classNames(
return twMerge(
backgroundClass,
hoverClass,
shadowClass,
props.pill.value ? '!rounded-full' : '',
props.disabled.value ? 'cursor-not-allowed opacity-50' : '',
props.pill.value && '!rounded-full',
props.disabled.value && 'cursor-not-allowed opacity-50',
isGradient && isOutline ? 'p-0.5' : sizeClasses.value,
slots.prefix || slots.suffix || props.loading.value ? 'inline-flex items-center' : '',
(slots.prefix || slots.suffix || props.loading.value) && 'inline-flex items-center',
)
})
const spanClasses = computed(() => {
if (!!props.gradient.value && props.outline.value) {
// ONLY FOR GRADIENT OUTLINE BUTTON
return classNames(
return twMerge(
'relative bg-white dark:bg-gray-900 rounded-md inline-flex items-center',
sizeClasses.value,
!props.disabled.value ? 'group-hover:bg-opacity-0 transition-all ease-in duration-75' : '',