CommonSimpleTable.vue 4.2 KB

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