CommonSimpleTable.vue 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. <!-- Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/ -->
  2. <script setup lang="ts">
  3. import { ref } from 'vue'
  4. import CommonActionMenu from '#desktop/components/CommonActionMenu/CommonActionMenu.vue'
  5. import type { MenuItem } from '#desktop/components/CommonPopoverMenu/types.ts'
  6. import type { TableHeader, TableItem } from './types.ts'
  7. export interface Props {
  8. headers: TableHeader[]
  9. items: TableItem[]
  10. actions?: MenuItem[]
  11. }
  12. defineProps<Props>()
  13. // :INFO - This would only would work on runtime, when keys are computed
  14. // :TODO - Find a way to infer the types on compile time or remove it completely
  15. // defineSlots<{
  16. // [key: `column-cell-${TableHeader['key']}`]: [
  17. // { item: TableHeader; header: TableHeader },
  18. // ]
  19. // [key: `header-suffix-${TableHeader['key']}`]: [{ item: TableHeader }]
  20. // [key: `item-suffix-${TableHeader['key']}`]: [{ item: TableHeader }]
  21. // test: []
  22. // }>()
  23. // Styling
  24. const cellAlignmentClasses = {
  25. right: 'text-right',
  26. center: 'text-center',
  27. left: 'text-left',
  28. }
  29. const rowBackgroundClasses = 'bg-blue-200 dark:bg-gray-700'
  30. const columnSeparatorClasses =
  31. 'border-r border-neutral-100 dark:border-gray-900'
  32. const contentCells = ref()
  33. const getTooltipText = (item: TableItem, header: TableHeader) => {
  34. return header.truncate ? item[header.key] : undefined
  35. }
  36. </script>
  37. <template>
  38. <table class="pb-3">
  39. <thead>
  40. <th
  41. v-for="header in headers"
  42. :key="header.key"
  43. class="h-10 p-2.5 text-xs font-normal text-stone-200 ltr:text-left rtl:text-right dark:text-neutral-500"
  44. :class="[
  45. header.columnClass,
  46. header.columnSeparator && columnSeparatorClasses,
  47. ]"
  48. >
  49. <CommonLabel
  50. class="font-normal text-stone-200 dark:text-neutral-500"
  51. :class="[cellAlignmentClasses[header.alignContent || 'left']]"
  52. size="small"
  53. >{{
  54. $t(header.label, ...(header.labelPlaceholder || []))
  55. }}</CommonLabel
  56. >
  57. <slot :name="`header-suffix-${header.key}`" :item="header" />
  58. </th>
  59. <th v-if="actions" class="h-10 w-0 p-2.5 text-center">
  60. <CommonLabel
  61. class="font-normal text-stone-200 dark:text-neutral-500"
  62. size="small"
  63. >{{ $t('Actions') }}</CommonLabel
  64. >
  65. </th>
  66. </thead>
  67. <tbody>
  68. <tr v-for="(item, index) in items" :key="item.id">
  69. <td
  70. v-for="header in headers"
  71. :key="`${item.id}-${header.key}`"
  72. class="h-10 p-2.5 text-sm first:rounded-s-md last:rounded-e-md"
  73. :class="[
  74. (index + 1) % 2 && rowBackgroundClasses,
  75. header.columnSeparator && columnSeparatorClasses,
  76. cellAlignmentClasses[header.alignContent || 'left'],
  77. {
  78. 'max-w-32 truncate text-black dark:text-white': header.truncate,
  79. },
  80. ]"
  81. >
  82. <slot
  83. :name="`column-cell-${header.key}`"
  84. :item="item"
  85. :header="header"
  86. >
  87. <CommonLabel
  88. ref="contentCells"
  89. v-tooltip.truncate="getTooltipText(item, header)"
  90. class="inline text-black dark:text-white"
  91. >
  92. <template v-if="!item[header.key]">-</template>
  93. <template v-else-if="header.type === 'timestamp_absolute'">
  94. <CommonDateTime
  95. :date-time="item[header.key] as string"
  96. type="absolute"
  97. />
  98. </template>
  99. <template v-else-if="header.type === 'timestamp'">
  100. <CommonDateTime :date-time="item[header.key] as string" />
  101. </template>
  102. <template v-else>
  103. {{ item[header.key] }}
  104. </template>
  105. </CommonLabel>
  106. </slot>
  107. <slot :name="`item-suffix-${header.key}`" :item="item" />
  108. </td>
  109. <td
  110. v-if="actions"
  111. class="h-10 p-2.5 text-center first:rounded-s-md last:rounded-e-md"
  112. :class="{ 'bg-blue-200 dark:bg-gray-700': (index + 1) % 2 }"
  113. >
  114. <slot name="actions" v-bind="{ actions, item }">
  115. <CommonActionMenu
  116. class="flex items-center justify-center"
  117. :actions="actions"
  118. :entity="item"
  119. button-size="medium"
  120. />
  121. </slot>
  122. </td>
  123. </tr>
  124. </tbody>
  125. </table>
  126. </template>