CommonIcon.vue 1.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. <!-- Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/ -->
  2. <script setup lang="ts">
  3. import { computed } from 'vue'
  4. import type { Animations, Sizes } from './types'
  5. export interface Props {
  6. size?: Sizes
  7. fixedSize?: { width: number; height: number }
  8. name: string
  9. decorative?: boolean
  10. animation?: Animations
  11. }
  12. const animationClassMap: Record<Animations, string> = {
  13. pulse: 'animate-pulse',
  14. spin: 'animate-spin',
  15. ping: 'animate-ping',
  16. bounce: 'animate-bounce',
  17. }
  18. const sizeMap: Record<Sizes, number> = {
  19. tiny: 15,
  20. small: 20,
  21. base: 25,
  22. medium: 30,
  23. large: 40,
  24. }
  25. const props = withDefaults(defineProps<Props>(), {
  26. size: 'medium',
  27. decorative: false,
  28. })
  29. const emit = defineEmits<{
  30. (e: 'click', event: MouseEvent): void
  31. }>()
  32. const onClick = (event: MouseEvent) => {
  33. emit('click', event)
  34. }
  35. const iconClass = computed(() => {
  36. let className = `icon-${props.name}`
  37. if (props.animation) {
  38. className += ` ${animationClassMap[props.animation]}`
  39. }
  40. return className
  41. })
  42. const finalSize = computed(() => {
  43. if (props.fixedSize) return props.fixedSize
  44. return {
  45. width: sizeMap[props.size],
  46. height: sizeMap[props.size],
  47. }
  48. })
  49. </script>
  50. <template>
  51. <svg
  52. xmlns="http://www.w3.org/2000/svg"
  53. class="icon fill-current"
  54. :class="iconClass"
  55. :width="finalSize.width"
  56. :height="finalSize.height"
  57. :aria-labelledby="name"
  58. :aria-hidden="decorative"
  59. @click="onClick"
  60. >
  61. <use :href="`#icon-${name}`" />
  62. </svg>
  63. </template>