184 lines
4.4 KiB
Vue
184 lines
4.4 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
|
|
gradient?: boolean
|
|
useGameFont?: 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,
|
|
gradient: false,
|
|
useGameFont: false,
|
|
})
|
|
|
|
const gameDataStore = useGameDataStore()
|
|
const { fontFamily } = storeToRefs(gameDataStore)
|
|
|
|
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 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 {}
|
|
})
|
|
const textColor = computed(() => {
|
|
return props.textColor ? props.textColor : 'var(--text-secondary)'
|
|
})
|
|
const buttonStyle = computed(() => {
|
|
const backgroundColor =
|
|
props.variant === 'filled' ? 'var(--primary)' : 'white'
|
|
const style: Record<string, string> = {
|
|
backgroundColor: props.backgroundColor ?? backgroundColor,
|
|
'--disabled-color': textColor.value,
|
|
}
|
|
|
|
return style
|
|
})
|
|
const textStyle = computed(() => {
|
|
const style: Record<string, string> = {
|
|
color: textColor.value,
|
|
}
|
|
|
|
if (props.useGameFont && fontFamily.value) {
|
|
style.fontFamily = fontFamily.value
|
|
}
|
|
|
|
return style
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<component
|
|
:is="componentTag"
|
|
v-bind="{ ...componentProps }"
|
|
:class="['btn-base', props.size, { disabled: props.disabled }]"
|
|
:data-variant="props.variant"
|
|
:style="buttonStyle"
|
|
:disabled="props.disabled"
|
|
>
|
|
<i v-if="props.gradient" class="btn-gradient"></i>
|
|
<span class="btn-content" :style="textStyle">
|
|
<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[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];
|
|
}
|
|
.btn-base.disabled {
|
|
@apply cursor-default pointer-events-none
|
|
after:opacity-20 after:z-[2];
|
|
}
|
|
.btn-base.disabled::after {
|
|
background-color: var(--disabled-color) !important;
|
|
}
|
|
|
|
.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;
|
|
}
|
|
|
|
.btn-gradient {
|
|
@apply absolute top-0 left-0 w-full h-full opacity-[0.7] mix-blend-soft-light;
|
|
background: radial-gradient(
|
|
68.19% 81.25% at 50.35% 100%,
|
|
#fff 20%,
|
|
rgba(255, 255, 255, 0) 100%
|
|
);
|
|
}
|
|
</style>
|