148 lines
3.3 KiB
Vue
148 lines
3.3 KiB
Vue
<template>
|
|
<component class="btn" :is="tag">
|
|
<span v-if="arrow" class="leading-icon">→</span>
|
|
<span class="text">
|
|
<span><slot /></span>
|
|
<span><slot /></span>
|
|
</span>
|
|
<span v-if="arrow" class="arrow">→</span>
|
|
</component>
|
|
</template>
|
|
|
|
<script setup>
|
|
const props = defineProps({
|
|
tag: {
|
|
type: String,
|
|
default: () => 'button',
|
|
},
|
|
arrow: {
|
|
type: Boolean,
|
|
default: () => false,
|
|
},
|
|
loading: {
|
|
type: Boolean,
|
|
default: () => false,
|
|
},
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.btn {
|
|
--t-duration: 350ms;
|
|
--t-ease: var(--ease-out-quad);
|
|
display: inline-flex;
|
|
align-items: center;
|
|
padding: rs(6px) rs(16px);
|
|
background: var(--theme-contrast);
|
|
color: var(--ivory);
|
|
border-radius: rs(8px);
|
|
position: relative;
|
|
overflow: hidden;
|
|
cursor: pointer;
|
|
transition: color var(--t-duration) var(--t-ease);
|
|
|
|
.leading-icon {
|
|
position: absolute;
|
|
width: rs(10px);
|
|
height: 1em;
|
|
display: flex;
|
|
align-items: center;
|
|
transform: translateX(-100%);
|
|
opacity: 0;
|
|
transition:
|
|
transform var(--t-duration) var(--t-ease),
|
|
opacity var(--t-duration) var(--t-ease);
|
|
}
|
|
.text {
|
|
position: relative;
|
|
display: block;
|
|
width: 100%;
|
|
overflow: hidden;
|
|
transition: transform var(--t-duration) var(--t-ease);
|
|
|
|
span {
|
|
display: inline-block;
|
|
transition: transform var(--t-duration) var(--t-ease);
|
|
@include label;
|
|
}
|
|
span:first-child {
|
|
color: var(--ivory);
|
|
}
|
|
span:last-child {
|
|
position: absolute;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
transform: translateY(105%);
|
|
color: var(--ivory);
|
|
}
|
|
}
|
|
.arrow {
|
|
transition:
|
|
transform var(--t-duration) var(--t-ease),
|
|
opacity var(--t-duration) var(--t-ease);
|
|
|
|
&:not(:only-child) {
|
|
margin-left: 0.5em;
|
|
}
|
|
}
|
|
.loading {
|
|
width: rs(10px);
|
|
height: 1em;
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
svg {
|
|
width: 100%;
|
|
height: auto;
|
|
}
|
|
}
|
|
|
|
&::before {
|
|
content: '';
|
|
display: block;
|
|
position: absolute;
|
|
inset: -2px;
|
|
background: var(--theme-contrast);
|
|
clip-path: inset(100% 0 0 0);
|
|
transition: clip-path var(--t-duration) var(--t-ease);
|
|
}
|
|
|
|
&:hover,
|
|
&.active,
|
|
&.router-link-active {
|
|
color: var(--ivory);
|
|
|
|
&::before {
|
|
clip-path: inset(0);
|
|
}
|
|
.text {
|
|
span:first-child {
|
|
transform: translateY(-105%);
|
|
}
|
|
span:last-child {
|
|
transform: none;
|
|
}
|
|
}
|
|
&.text-icon {
|
|
.leading-icon {
|
|
transform: none;
|
|
opacity: 1;
|
|
}
|
|
.text {
|
|
transform: translateX(1.5em);
|
|
}
|
|
.arrow {
|
|
transform: translateX(100%);
|
|
opacity: 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
&:disabled,
|
|
&.disabled {
|
|
opacity: 0.16;
|
|
pointer-events: none;
|
|
}
|
|
}
|
|
</style>
|