Skip to content

Commit 061eebb

Browse files
committed
Add ActionButton component
1 parent 0abcab9 commit 061eebb

File tree

1 file changed

+97
-0
lines changed

1 file changed

+97
-0
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<script setup lang="ts">
2+
import FAIcon from "@/components/FAIcon.vue";
3+
import { IconDefinition } from "@fortawesome/free-solid-svg-icons";
4+
5+
export type ButtonVariant = "primary" | "secondary" | "danger" | "link" | "default";
6+
export type ButtonSize = "sm" | "lg" | "default";
7+
8+
interface Props {
9+
variant?: ButtonVariant;
10+
size?: ButtonSize;
11+
icon?: IconDefinition;
12+
iconPosition?: "left" | "right";
13+
disabled?: boolean;
14+
loading?: boolean;
15+
tooltip?: string;
16+
ariaLabel?: string;
17+
type?: "button" | "submit" | "reset";
18+
}
19+
20+
const props = withDefaults(defineProps<Props>(), {
21+
variant: "default",
22+
size: "default",
23+
iconPosition: "left",
24+
disabled: false,
25+
loading: false,
26+
type: "button",
27+
});
28+
29+
const variantClasses = {
30+
primary: "btn-primary",
31+
secondary: "btn-secondary",
32+
danger: "btn-danger",
33+
link: "btn-link",
34+
default: "btn-default",
35+
};
36+
37+
const sizeClasses = {
38+
sm: "btn-sm",
39+
lg: "btn-lg",
40+
default: "",
41+
};
42+
43+
const buttonClasses = ["btn", variantClasses[props.variant], sizeClasses[props.size]].filter(Boolean).join(" ");
44+
</script>
45+
46+
<template>
47+
<button :class="[buttonClasses, { disabled: disabled || loading }]" :disabled="disabled || loading" :type="type" :aria-label="ariaLabel" v-tippy="tooltip">
48+
<FAIcon v-if="icon && iconPosition === 'left' && !loading" :icon="icon" class="icon-left" />
49+
50+
<i v-if="loading" class="glyphicon glyphicon-refresh rotate"></i>
51+
52+
<span v-if="$slots.default" class="button-text">
53+
<slot />
54+
</span>
55+
56+
<FAIcon v-if="icon && iconPosition === 'right' && !loading" :icon="icon" class="icon-right" />
57+
</button>
58+
</template>
59+
60+
<style scoped>
61+
.btn {
62+
display: inline-flex;
63+
align-items: center;
64+
gap: 0.375rem;
65+
cursor: pointer;
66+
}
67+
68+
.btn.disabled {
69+
cursor: not-allowed;
70+
opacity: 0.65;
71+
}
72+
73+
.icon-left {
74+
margin-right: 0.25rem;
75+
}
76+
77+
.icon-right {
78+
margin-left: 0.25rem;
79+
}
80+
81+
.rotate {
82+
animation: spin 1s linear infinite;
83+
}
84+
85+
@keyframes spin {
86+
from {
87+
transform: rotate(0deg);
88+
}
89+
to {
90+
transform: rotate(360deg);
91+
}
92+
}
93+
94+
.button-text {
95+
flex: 1;
96+
}
97+
</style>

0 commit comments

Comments
 (0)