Files
web-temp/layers/components/atoms/Button/index.vue
2026-01-16 14:55:02 +09:00

135 lines
3.2 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 'AtomsLocaleLink'
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') {
return {
to: props.href,
}
}
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"
/>
<AtomsIconsWebLinkLine
v-if="props.type === 'external'"
:color="props.textColor"
class="icon"
/>
<AtomsIconsDownloadLine
v-if="props.type === 'download'"
:color="props.textColor"
class="icon"
/>
</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];
}
.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>