CommonButton.vue 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. <!-- Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/ -->
  2. <script setup lang="ts">
  3. import { computed } from 'vue'
  4. import { startCase } from 'lodash-es'
  5. import type { ButtonSize, ButtonType, ButtonVariant } from './types.ts'
  6. interface Props {
  7. variant?: ButtonVariant
  8. type?: ButtonType
  9. disabled?: boolean
  10. block?: boolean
  11. form?: string
  12. size?: ButtonSize
  13. prefixIcon?: string
  14. icon?: string
  15. suffixIcon?: string
  16. }
  17. const props = withDefaults(defineProps<Props>(), {
  18. variant: 'secondary',
  19. type: 'button',
  20. size: 'small',
  21. })
  22. const variantClasses = computed(() => {
  23. switch (props.variant) {
  24. case 'primary':
  25. return ['btn-primary', 'bg-blue-800', 'hover:bg-blue-800', 'text-white']
  26. case 'tertiary':
  27. return [
  28. 'btn-neutral',
  29. 'bg-green-200',
  30. 'hover:bg-green-200',
  31. 'dark:bg-gray-600',
  32. 'dark:hover:bg-gray-600',
  33. 'text-gray-300',
  34. 'dark:text-neutral-400',
  35. ]
  36. case 'submit':
  37. return [
  38. 'btn-accent',
  39. 'bg-yellow-300',
  40. 'hover:bg-yellow-300',
  41. 'text-black',
  42. ]
  43. case 'danger':
  44. return [
  45. 'btn-error',
  46. 'bg-pink-100',
  47. 'hover:bg-pink-100',
  48. 'dark:bg-red-900',
  49. 'dark:hover:bg-red-900',
  50. 'text-red-500',
  51. ]
  52. case 'remove':
  53. return [
  54. 'btn-info',
  55. 'bg-red-400',
  56. 'hover:bg-red-400',
  57. 'dark:bg-red-600',
  58. 'dark:hover:bg-red-600',
  59. 'text-white',
  60. ]
  61. case 'secondary':
  62. default:
  63. return [
  64. 'btn-secondary',
  65. 'bg-transparent',
  66. 'hover:bg-transparent',
  67. 'text-blue-800',
  68. ]
  69. }
  70. })
  71. const sizeClasses = computed(() => {
  72. switch (props.size) {
  73. case 'large':
  74. return ['btn-lg', 'text-base']
  75. case 'medium':
  76. return ['btn-md', 'text-sm']
  77. case 'small':
  78. default:
  79. return ['btn-sm', 'text-xs']
  80. }
  81. })
  82. const paddingClasses = computed(() => {
  83. if (props.icon) return ['p-1']
  84. switch (props.size) {
  85. case 'large':
  86. return ['px-4', 'py-2.5']
  87. case 'medium':
  88. return ['px-3', 'py-2']
  89. case 'small':
  90. default:
  91. return ['px-2.5', 'py-1.5']
  92. }
  93. })
  94. const disabledClasses = computed(() => {
  95. if (!props.disabled) return []
  96. return ['opacity-30', 'pointer-events-none']
  97. })
  98. const borderRadiusClass = computed(() => {
  99. switch (props.size) {
  100. case 'large':
  101. if (props.icon) return 'rounded-lg'
  102. return 'rounded-xl'
  103. case 'medium':
  104. return 'rounded-lg'
  105. case 'small':
  106. default:
  107. return 'rounded-md'
  108. }
  109. })
  110. const iconSizeClass = computed(() => {
  111. switch (props.size) {
  112. case 'large':
  113. return 'small'
  114. case 'medium':
  115. return 'tiny'
  116. case 'small':
  117. default:
  118. return 'xs'
  119. }
  120. })
  121. </script>
  122. <template>
  123. <button
  124. class="btn h-min min-h-min border-0 shadow-none font-normal flex-nowrap gap-x-2.5 hover:outline hover:outline-1 hover:outline-offset-1 hover:outline-blue-600 dark:hover:outline-blue-900 focus-visible:outline-1 focus-visible:outline-offset-1 focus-visible:outline-blue-800"
  125. :class="[
  126. ...variantClasses,
  127. ...sizeClasses,
  128. ...paddingClasses,
  129. ...disabledClasses,
  130. borderRadiusClass,
  131. {
  132. 'btn-block': block,
  133. 'w-min': !block,
  134. },
  135. ]"
  136. :type="type"
  137. :form="form"
  138. :aria-disabled="disabled ? 'true' : undefined"
  139. >
  140. <CommonIcon
  141. v-if="prefixIcon"
  142. class="shrink-0"
  143. decorative
  144. :size="iconSizeClass"
  145. :name="prefixIcon"
  146. />
  147. <CommonIcon
  148. v-if="icon"
  149. class="shrink-0"
  150. decorative
  151. :size="iconSizeClass"
  152. :name="icon"
  153. />
  154. <span v-else class="truncate">
  155. <slot>{{ $t(startCase(variant)) }}</slot>
  156. </span>
  157. <CommonIcon
  158. v-if="suffixIcon"
  159. class="shrink-0"
  160. decorative
  161. :size="iconSizeClass"
  162. :name="suffixIcon"
  163. />
  164. </button>
  165. </template>