CommonSimpleTable.vue 4.3 KB

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