|
|
<template> |
|
|
<i |
|
|
:class="iconClass" |
|
|
:style="iconStyle" |
|
|
@click="handleClick" |
|
|
></i> |
|
|
</template> |
|
|
|
|
|
<script setup> |
|
|
import { computed } from 'vue' |
|
|
|
|
|
const props = defineProps({ |
|
|
|
|
|
name: { |
|
|
type: String, |
|
|
required: true |
|
|
}, |
|
|
|
|
|
|
|
|
size: { |
|
|
type: [String, Number], |
|
|
default: 'inherit' |
|
|
}, |
|
|
|
|
|
|
|
|
color: { |
|
|
type: String, |
|
|
default: 'inherit' |
|
|
}, |
|
|
|
|
|
|
|
|
clickable: { |
|
|
type: Boolean, |
|
|
default: false |
|
|
}, |
|
|
|
|
|
|
|
|
rotate: { |
|
|
type: [String, Number], |
|
|
default: 0 |
|
|
}, |
|
|
|
|
|
|
|
|
loading: { |
|
|
type: Boolean, |
|
|
default: false |
|
|
} |
|
|
}) |
|
|
|
|
|
const emit = defineEmits(['click']) |
|
|
|
|
|
|
|
|
const iconClass = computed(() => { |
|
|
const classes = [] |
|
|
|
|
|
|
|
|
if (props.name.startsWith('fa-')) { |
|
|
classes.push('fas', props.name) |
|
|
} else { |
|
|
classes.push('fas', `fa-${props.name}`) |
|
|
} |
|
|
|
|
|
|
|
|
if (props.clickable) { |
|
|
classes.push('icon-clickable') |
|
|
} |
|
|
|
|
|
|
|
|
if (props.loading) { |
|
|
classes.push('fa-spin') |
|
|
} |
|
|
|
|
|
return classes.join(' ') |
|
|
}) |
|
|
|
|
|
|
|
|
const iconStyle = computed(() => { |
|
|
const styles = {} |
|
|
|
|
|
|
|
|
if (props.size !== 'inherit') { |
|
|
const sizeValue = typeof props.size === 'number' ? `${props.size}px` : props.size |
|
|
styles.fontSize = sizeValue |
|
|
} |
|
|
|
|
|
|
|
|
if (props.color !== 'inherit') { |
|
|
styles.color = props.color |
|
|
} |
|
|
|
|
|
|
|
|
if (props.rotate && !props.loading) { |
|
|
styles.transform = `rotate(${props.rotate}deg)` |
|
|
} |
|
|
|
|
|
return styles |
|
|
}) |
|
|
|
|
|
|
|
|
const handleClick = (event) => { |
|
|
if (props.clickable) { |
|
|
emit('click', event) |
|
|
} |
|
|
} |
|
|
</script> |
|
|
|
|
|
<style scoped> |
|
|
.icon-clickable { |
|
|
cursor: pointer; |
|
|
transition: var(--transition-fast); |
|
|
user-select: none; |
|
|
} |
|
|
|
|
|
.icon-clickable:hover { |
|
|
opacity: 0.8; |
|
|
transform: scale(1.05); |
|
|
} |
|
|
|
|
|
.icon-clickable:active { |
|
|
transform: scale(0.95); |
|
|
} |
|
|
|
|
|
|
|
|
.fa-play { |
|
|
margin-left: 2px; |
|
|
} |
|
|
|
|
|
.fa-heart { |
|
|
color: #e53e3e; |
|
|
} |
|
|
|
|
|
.fa-heart.far { |
|
|
color: var(--text-secondary); |
|
|
} |
|
|
|
|
|
.fa-star { |
|
|
color: #ed8936; |
|
|
} |
|
|
|
|
|
.fa-star.far { |
|
|
color: var(--text-secondary); |
|
|
} |
|
|
|
|
|
|
|
|
.fa-spin { |
|
|
animation: fa-spin 1s infinite linear; |
|
|
} |
|
|
|
|
|
@keyframes fa-spin { |
|
|
0% { |
|
|
transform: rotate(0deg); |
|
|
} |
|
|
100% { |
|
|
transform: rotate(360deg); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.icon-clickable:focus-visible { |
|
|
outline: 2px solid var(--accent-red); |
|
|
outline-offset: 2px; |
|
|
border-radius: 2px; |
|
|
} |
|
|
|
|
|
|
|
|
.icon-disabled { |
|
|
opacity: 0.3; |
|
|
cursor: not-allowed; |
|
|
pointer-events: none; |
|
|
} |
|
|
</style> |