150 lines
3.6 KiB
Vue
150 lines
3.6 KiB
Vue
<script setup lang="ts">
|
|
import type { ButtonType, ButtonVariant } from '#layers/types/components/button'
|
|
|
|
interface props {
|
|
type?: ButtonType
|
|
size?: string
|
|
variant?: ButtonVariant
|
|
target?: '_self' | '_blank'
|
|
href?: string
|
|
backgroundColor?: string
|
|
textColor?: string
|
|
disabled?: boolean
|
|
}
|
|
|
|
const props = withDefaults(defineProps<props>(), {
|
|
type: 'action',
|
|
size: 'size-extra-small md:size-medium',
|
|
variant: 'filled',
|
|
target: '_self',
|
|
textColor: 'var(--alternative-02)',
|
|
disabled: false,
|
|
})
|
|
|
|
const componentTag = computed((): string => {
|
|
switch (props.type) {
|
|
case 'external':
|
|
case 'link':
|
|
return 'a'
|
|
case 'download':
|
|
return props.href ? 'a' : 'button'
|
|
case 'internal':
|
|
return props.href ? 'AtomsLocaleLink' : 'button'
|
|
default:
|
|
return 'button'
|
|
}
|
|
})
|
|
const backgroundColor = computed(() => {
|
|
if (props.backgroundColor) {
|
|
return props.backgroundColor
|
|
}
|
|
return props.variant === 'filled' ? 'var(--primary)' : 'white'
|
|
})
|
|
const componentProps = computed(() => {
|
|
if (props.type === 'external' || props.type === 'link') {
|
|
return {
|
|
href: props.href,
|
|
target: props.target,
|
|
}
|
|
}
|
|
|
|
if (props.type === 'internal') {
|
|
if (props.href) {
|
|
return {
|
|
to: props.href,
|
|
}
|
|
}
|
|
return {}
|
|
}
|
|
|
|
if (props.type === 'download') {
|
|
if (props.href) {
|
|
return {
|
|
href: props.href,
|
|
target: '_self',
|
|
download: props.href?.split('/').pop() ?? 'download',
|
|
}
|
|
}
|
|
return {}
|
|
}
|
|
|
|
return {}
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<component
|
|
:is="componentTag"
|
|
v-bind="{ ...componentProps }"
|
|
:class="['btn-base', props.size, { disabled: props.disabled }]"
|
|
:data-variant="props.variant"
|
|
:style="{
|
|
backgroundColor: backgroundColor,
|
|
color: props.textColor ? props.textColor : 'var(--text-secondary)',
|
|
'--text-color': props.textColor,
|
|
}"
|
|
:disabled="props.disabled"
|
|
>
|
|
<span class="btn-content">
|
|
<slot />
|
|
<AtomsIconsLongArrowRightLine
|
|
v-if="props.type === 'internal'"
|
|
:color="props.textColor"
|
|
class="icon icon-internal"
|
|
/>
|
|
<AtomsIconsWebLinkLine
|
|
v-if="props.type === 'external'"
|
|
:color="props.textColor"
|
|
class="icon icon-external"
|
|
/>
|
|
<AtomsIconsDownloadLine
|
|
v-if="props.type === 'download'"
|
|
:color="props.textColor"
|
|
class="icon icon-download"
|
|
/>
|
|
</span>
|
|
</component>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.btn-base {
|
|
@apply overflow-hidden relative inline-flex items-center justify-center font-medium cursor-pointer
|
|
before:content-[''] before:absolute before:top-0 before:left-0 before:w-full before:h-full before:border before:border-white/10;
|
|
}
|
|
.btn-base.disabled {
|
|
@apply cursor-default pointer-events-none
|
|
after:bg-[var(--text-color)] after:opacity-20 after:z-[2];
|
|
}
|
|
|
|
.btn-base[data-variant='filled'] {
|
|
@apply after:content-[''] after:absolute after:top-0 after:left-0 after:w-full after:h-full after:bg-white after:transition-opacity after:duration-300 after:ease-in-out after:opacity-0
|
|
hover:after:opacity-20;
|
|
}
|
|
.btn-base[data-variant='outlined'] {
|
|
@apply before:border-[rgba(0,0,0,0.1)]
|
|
hover:before:border-[#999];
|
|
}
|
|
.icon {
|
|
@apply transition-transform duration-300 ease-spring;
|
|
}
|
|
.btn-base:hover .icon-internal {
|
|
@apply translate-x-[3px];
|
|
}
|
|
.btn-base:hover .icon-external {
|
|
@apply scale-[1.08];
|
|
}
|
|
.btn-base:hover .icon-download {
|
|
@apply translate-y-[3px];
|
|
}
|
|
|
|
.btn-base.disabled .btn-content {
|
|
@apply opacity-50;
|
|
}
|
|
.btn-base .btn-content {
|
|
@apply relative flex items-center gap-1 z-[1];
|
|
}
|
|
.btn-base.size-extra-small .btn-content {
|
|
@apply gap-0.5;
|
|
}
|
|
</style>
|