feat: Added href prop to button component
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
8
docs/components/button/examples/ButtonLinkExample.vue
Normal file
8
docs/components/button/examples/ButtonLinkExample.vue
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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' : '',
|
||||
|
||||
Reference in New Issue
Block a user