composable.ts 1.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. // Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/
  2. import { i18n } from '@shared/i18n'
  3. import { computed } from 'vue'
  4. import type { Props } from './CommonIcon.vue'
  5. import type { Animations, Sizes } from './types'
  6. export const usePrivateIcon = (
  7. props: Omit<Props, 'size'> & { size: Sizes },
  8. ) => {
  9. const animationClassMap: Record<Animations, string> = {
  10. pulse: 'animate-pulse',
  11. spin: 'animate-spin',
  12. ping: 'animate-ping',
  13. bounce: 'animate-bounce',
  14. }
  15. const sizeMap: Record<Sizes, number> = {
  16. xs: 12,
  17. tiny: 16,
  18. small: 20,
  19. base: 24,
  20. medium: 32,
  21. large: 48,
  22. xl: 96,
  23. }
  24. const iconClass = computed(() => {
  25. let className = `icon-${props.name}`
  26. if (props.animation) {
  27. className += ` ${animationClassMap[props.animation]}`
  28. }
  29. return className
  30. })
  31. const finalSize = computed(() => {
  32. if (props.fixedSize) return props.fixedSize
  33. return {
  34. width: sizeMap[props.size],
  35. height: sizeMap[props.size],
  36. }
  37. })
  38. return {
  39. iconClass,
  40. finalSize,
  41. }
  42. }
  43. export const useRawHTMLIcon = (props: Props & { class?: string }) => {
  44. const { iconClass, finalSize } = usePrivateIcon({ size: 'medium', ...props })
  45. const html = String.raw
  46. return html`
  47. <svg
  48. xmlns="http://www.w3.org/2000/svg"
  49. class="icon ${iconClass.value} ${props.class || ''} fill-current"
  50. width="${finalSize.value.width}"
  51. height="${finalSize.value.height}"
  52. ${!props.decorative &&
  53. `aria-label=${i18n.t(props.label || props.name) || ''}`}
  54. ${(props.decorative && 'aria-hidden="true"') || ''}
  55. >
  56. <use href="#icon-${props.name}" />
  57. </svg>
  58. `
  59. }