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

View File

@@ -2,6 +2,7 @@ import type { Ref } from 'vue'
import { computed, useSlots } from 'vue' import { computed, useSlots } from 'vue'
import classNames from 'classnames' import classNames from 'classnames'
import type { ButtonDuotoneGradient, ButtonGradient, ButtonMonochromeGradient, ButtonSize, ButtonVariant } from '../types' 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> } 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, backgroundClass,
hoverClass, hoverClass,
shadowClass, shadowClass,
props.pill.value ? '!rounded-full' : '', props.pill.value && '!rounded-full',
props.disabled.value ? 'cursor-not-allowed opacity-50' : '', props.disabled.value && 'cursor-not-allowed opacity-50',
isGradient && isOutline ? 'p-0.5' : sizeClasses.value, 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(() => { const spanClasses = computed(() => {
if (!!props.gradient.value && props.outline.value) { if (!!props.gradient.value && props.outline.value) {
// ONLY FOR GRADIENT OUTLINE BUTTON // ONLY FOR GRADIENT OUTLINE BUTTON
return classNames( return twMerge(
'relative bg-white dark:bg-gray-900 rounded-md inline-flex items-center', 'relative bg-white dark:bg-gray-900 rounded-md inline-flex items-center',
sizeClasses.value, sizeClasses.value,
!props.disabled.value ? 'group-hover:bg-opacity-0 transition-all ease-in duration-75' : '', !props.disabled.value ? 'group-hover:bg-opacity-0 transition-all ease-in duration-75' : '',