@meistrari/tela-build 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (295) hide show
  1. package/README.md +75 -0
  2. package/app.config.ts +73 -0
  3. package/components/tela/animated/animated-calculating-number.vue +16 -0
  4. package/components/tela/animated/animated-number.mdx +248 -0
  5. package/components/tela/animated/animated-number.stories.ts +52 -0
  6. package/components/tela/animated/animated-number.vue +23 -0
  7. package/components/tela/animated/animated-text.vue +124 -0
  8. package/components/tela/animated/animated-value.vue +68 -0
  9. package/components/tela/avatar/avatar.mdx +117 -0
  10. package/components/tela/avatar/avatar.stories.ts +62 -0
  11. package/components/tela/avatar/avatar.vue +71 -0
  12. package/components/tela/avatar/group/avatar-group.stories.ts +78 -0
  13. package/components/tela/avatar/group/avatar-group.vue +46 -0
  14. package/components/tela/badge/badge.mdx +154 -0
  15. package/components/tela/badge/badge.stories.ts +82 -0
  16. package/components/tela/badge/badge.vue +41 -0
  17. package/components/tela/button/button.mdx +155 -0
  18. package/components/tela/button/button.stories.ts +202 -0
  19. package/components/tela/button/button.vue +107 -0
  20. package/components/tela/card.vue +30 -0
  21. package/components/tela/chart/chart-bar.vue +58 -0
  22. package/components/tela/chat/chat.mdx +268 -0
  23. package/components/tela/chat/chat.stories.ts +253 -0
  24. package/components/tela/chat/command/index.vue +41 -0
  25. package/components/tela/chat/command/mention/index.vue +138 -0
  26. package/components/tela/chat/index.vue +112 -0
  27. package/components/tela/chat/pure-text-input/chat-text-input.vue +190 -0
  28. package/components/tela/chat/text-input/chat-text-input.stories.ts +128 -0
  29. package/components/tela/chat/text-input/index.vue +217 -0
  30. package/components/tela/chat/text-message/chat-text-message.stories.ts +138 -0
  31. package/components/tela/chat/text-message/index.vue +355 -0
  32. package/components/tela/chat/types.ts +19 -0
  33. package/components/tela/checkbox/checkbox-card.vue +30 -0
  34. package/components/tela/checkbox/checkbox.mdx +164 -0
  35. package/components/tela/checkbox/checkbox.stories.ts +104 -0
  36. package/components/tela/checkbox/checkbox.vue +43 -0
  37. package/components/tela/collapsible/Collapsible.vue +15 -0
  38. package/components/tela/collapsible/CollapsibleContent.vue +59 -0
  39. package/components/tela/collapsible/CollapsibleTrigger.vue +12 -0
  40. package/components/tela/collapsible/collapsible.mdx +157 -0
  41. package/components/tela/collapsible-section/collapsible-section.mdx +180 -0
  42. package/components/tela/collapsible-section/collapsible-section.stories.ts +53 -0
  43. package/components/tela/collapsible-section/collapsible-section.vue +51 -0
  44. package/components/tela/collapsible-section-with-actions.vue +98 -0
  45. package/components/tela/combobox/combobox-anchor.vue +24 -0
  46. package/components/tela/combobox/combobox-empty.vue +19 -0
  47. package/components/tela/combobox/combobox-group.vue +24 -0
  48. package/components/tela/combobox/combobox-indicator.vue +22 -0
  49. package/components/tela/combobox/combobox-input.vue +31 -0
  50. package/components/tela/combobox/combobox-item.vue +28 -0
  51. package/components/tela/combobox/combobox-label.vue +24 -0
  52. package/components/tela/combobox/combobox-list.vue +90 -0
  53. package/components/tela/combobox/combobox-module-selector.vue +366 -0
  54. package/components/tela/combobox/combobox-root.vue +15 -0
  55. package/components/tela/combobox/combobox-trigger.vue +12 -0
  56. package/components/tela/combobox/combobox.mdx +285 -0
  57. package/components/tela/combobox/combobox.stories.ts +232 -0
  58. package/components/tela/combobox/combobox.vue +497 -0
  59. package/components/tela/command/command-dialog.vue +22 -0
  60. package/components/tela/command/command-empty.vue +25 -0
  61. package/components/tela/command/command-group.vue +46 -0
  62. package/components/tela/command/command-input.vue +38 -0
  63. package/components/tela/command/command-item.vue +78 -0
  64. package/components/tela/command/command-list.vue +78 -0
  65. package/components/tela/command/command-separator.vue +23 -0
  66. package/components/tela/command/command-shortcut.vue +13 -0
  67. package/components/tela/command/command.vue +88 -0
  68. package/components/tela/command/dialog-base.vue +15 -0
  69. package/components/tela/command/dialog-content.vue +50 -0
  70. package/components/tela/command/utils.ts +15 -0
  71. package/components/tela/complex-table/complex-table-cell.stories.ts +145 -0
  72. package/components/tela/complex-table/complex-table-cell.vue +45 -0
  73. package/components/tela/complex-table/complex-table-header-cell.stories.ts +103 -0
  74. package/components/tela/complex-table/complex-table-header-cell.vue +48 -0
  75. package/components/tela/complex-table/complex-table-header.stories.ts +89 -0
  76. package/components/tela/complex-table/complex-table-header.vue +70 -0
  77. package/components/tela/complex-table/complex-table-row.vue +199 -0
  78. package/components/tela/complex-table/complex-table-virtualized.vue +326 -0
  79. package/components/tela/complex-table/complex-table.stories.ts +358 -0
  80. package/components/tela/complex-table/complex-table.vue +237 -0
  81. package/components/tela/complex-table/composables/table-common.ts +93 -0
  82. package/components/tela/complex-table/composables/table-selection.ts +87 -0
  83. package/components/tela/complex-table/composables/virtual-scroll.ts +252 -0
  84. package/components/tela/complex-table/styles/table-shared.css +170 -0
  85. package/components/tela/complex-table/types.ts +63 -0
  86. package/components/tela/complex-table/utils.ts +35 -0
  87. package/components/tela/confirm-button/confirm-button.vue +137 -0
  88. package/components/tela/confirmation-modal/confirmation-modal.vue +72 -0
  89. package/components/tela/copy-button.vue +86 -0
  90. package/components/tela/date-range-picker.vue +221 -0
  91. package/components/tela/dialog/dialog.mdx +170 -0
  92. package/components/tela/dialog/dialog.vue +182 -0
  93. package/components/tela/disabled-area.vue +16 -0
  94. package/components/tela/disclaimer/disclaimer.mdx +238 -0
  95. package/components/tela/disclaimer/disclaimer.stories.ts +196 -0
  96. package/components/tela/disclaimer/disclaimer.vue +125 -0
  97. package/components/tela/dropdown-menu/DropdownMenu.vue +121 -0
  98. package/components/tela/dropdown-menu/DropdownMenuCheckboxItem.vue +40 -0
  99. package/components/tela/dropdown-menu/DropdownMenuContent.vue +75 -0
  100. package/components/tela/dropdown-menu/DropdownMenuGroup.vue +12 -0
  101. package/components/tela/dropdown-menu/DropdownMenuItem.vue +137 -0
  102. package/components/tela/dropdown-menu/DropdownMenuLabel.vue +26 -0
  103. package/components/tela/dropdown-menu/DropdownMenuRadioGroup.vue +18 -0
  104. package/components/tela/dropdown-menu/DropdownMenuRadioItem.vue +40 -0
  105. package/components/tela/dropdown-menu/DropdownMenuRoot.vue +15 -0
  106. package/components/tela/dropdown-menu/DropdownMenuSeparator.vue +21 -0
  107. package/components/tela/dropdown-menu/DropdownMenuShortcut.vue +14 -0
  108. package/components/tela/dropdown-menu/DropdownMenuSub.vue +18 -0
  109. package/components/tela/dropdown-menu/DropdownMenuSubContent.vue +30 -0
  110. package/components/tela/dropdown-menu/DropdownMenuSubTrigger.vue +35 -0
  111. package/components/tela/dropdown-menu/DropdownMenuTrigger.vue +14 -0
  112. package/components/tela/dropdown-menu/dropdown-menu.mdx +265 -0
  113. package/components/tela/dropdown-menu/dropdown-menu.stories.ts +156 -0
  114. package/components/tela/expandable-input.vue +96 -0
  115. package/components/tela/file-drop.vue +37 -0
  116. package/components/tela/file-upload/file-upload.mdx +189 -0
  117. package/components/tela/file-upload/file-upload.stories.ts +48 -0
  118. package/components/tela/file-upload/file-upload.vue +205 -0
  119. package/components/tela/filters/checkbox-filter.stories.ts +218 -0
  120. package/components/tela/filters/checkbox-filter.vue +165 -0
  121. package/components/tela/filters/date-filter.stories.ts +258 -0
  122. package/components/tela/filters/date-filter.vue +200 -0
  123. package/components/tela/filters/user-filter.stories.ts +344 -0
  124. package/components/tela/filters/user-filter.vue +271 -0
  125. package/components/tela/hover-card/hover-card.mdx +221 -0
  126. package/components/tela/hover-card/hover-card.stories.ts +87 -0
  127. package/components/tela/hover-card/hover-card.vue +61 -0
  128. package/components/tela/icon/custom.vue +319 -0
  129. package/components/tela/icon/spinner.vue +12 -0
  130. package/components/tela/icon-button/icon-button.vue +114 -0
  131. package/components/tela/icon.vue +37 -0
  132. package/components/tela/initials.vue +28 -0
  133. package/components/tela/inline-input.vue +77 -0
  134. package/components/tela/input/input.mdx +182 -0
  135. package/components/tela/input/input.stories.ts +153 -0
  136. package/components/tela/input/tela-input.vue +240 -0
  137. package/components/tela/kbd/kbd-return.vue +6 -0
  138. package/components/tela/kbd/kbd.mdx +238 -0
  139. package/components/tela/kbd/kbd.vue +18 -0
  140. package/components/tela/label/label.mdx +121 -0
  141. package/components/tela/label/label.stories.ts +37 -0
  142. package/components/tela/label/label.vue +25 -0
  143. package/components/tela/link-decoration/link-decoration.vue +19 -0
  144. package/components/tela/live-label.vue +32 -0
  145. package/components/tela/long-press-button.vue +98 -0
  146. package/components/tela/menubar/menubar-content.vue +77 -0
  147. package/components/tela/menubar/menubar-item.vue +32 -0
  148. package/components/tela/menubar/menubar-label.vue +14 -0
  149. package/components/tela/menubar/menubar-menu.vue +12 -0
  150. package/components/tela/menubar/menubar-root.vue +30 -0
  151. package/components/tela/menubar/menubar-separator.vue +17 -0
  152. package/components/tela/menubar/menubar-shortcut.vue +14 -0
  153. package/components/tela/menubar/menubar-sub-content.vue +36 -0
  154. package/components/tela/menubar/menubar-sub-trigger.vue +28 -0
  155. package/components/tela/menubar/menubar-sub.vue +20 -0
  156. package/components/tela/menubar/menubar-trigger.vue +27 -0
  157. package/components/tela/menubar/menubar.vue +298 -0
  158. package/components/tela/modal/modal.mdx +145 -0
  159. package/components/tela/modal/modal.vue +242 -0
  160. package/components/tela/multiple-select/multiple-select.mdx +274 -0
  161. package/components/tela/multiple-select/multiple-select.stories.ts +325 -0
  162. package/components/tela/multiple-select/multiple-select.vue +666 -0
  163. package/components/tela/pane.vue +110 -0
  164. package/components/tela/popover/popover-content.vue +48 -0
  165. package/components/tela/popover/popover-trigger.vue +12 -0
  166. package/components/tela/popover/popover.mdx +239 -0
  167. package/components/tela/popover/popover.stories.ts +150 -0
  168. package/components/tela/popover/popover.vue +15 -0
  169. package/components/tela/popover-list/popover-list-nested.vue +104 -0
  170. package/components/tela/popover-list/popover-list.stories.ts +330 -0
  171. package/components/tela/popover-list/popover-list.vue +191 -0
  172. package/components/tela/radio-button.vue +66 -0
  173. package/components/tela/radio-group/radio-group-item.vue +40 -0
  174. package/components/tela/radio-group/radio-group-root.vue +26 -0
  175. package/components/tela/radio-group/radio-group.mdx +78 -0
  176. package/components/tela/radio-group/radio-group.stories.ts +106 -0
  177. package/components/tela/radio-group/radio-group.vue +23 -0
  178. package/components/tela/range-calendar.stories.ts +110 -0
  179. package/components/tela/range-calendar.vue +109 -0
  180. package/components/tela/scroll-area/scroll-area.mdx +183 -0
  181. package/components/tela/scroll-area/scroll-area.vue +30 -0
  182. package/components/tela/scroll-area/scroll-bar.vue +31 -0
  183. package/components/tela/segment-toggle.stories.ts +114 -0
  184. package/components/tela/segment-toggle.vue +66 -0
  185. package/components/tela/select-menu/select-menu-content.vue +106 -0
  186. package/components/tela/select-menu/select-menu-down-button.vue +20 -0
  187. package/components/tela/select-menu/select-menu-group.vue +16 -0
  188. package/components/tela/select-menu/select-menu-item.vue +40 -0
  189. package/components/tela/select-menu/select-menu-root.vue +15 -0
  190. package/components/tela/select-menu/select-menu-trigger.vue +34 -0
  191. package/components/tela/select-menu/select-menu-up-button.vue +20 -0
  192. package/components/tela/select-menu/select-menu-value.vue +12 -0
  193. package/components/tela/select-menu/select-menu.mdx +221 -0
  194. package/components/tela/select-menu/select-menu.stories.ts +91 -0
  195. package/components/tela/select-menu/select-menu.vue +165 -0
  196. package/components/tela/selector/selector.vue +47 -0
  197. package/components/tela/sheet/sheet-close.vue +12 -0
  198. package/components/tela/sheet/sheet-content.vue +57 -0
  199. package/components/tela/sheet/sheet-description.vue +23 -0
  200. package/components/tela/sheet/sheet-footer.vue +18 -0
  201. package/components/tela/sheet/sheet-header.vue +15 -0
  202. package/components/tela/sheet/sheet-root.vue +18 -0
  203. package/components/tela/sheet/sheet-title.vue +23 -0
  204. package/components/tela/sheet/sheet-trigger.vue +12 -0
  205. package/components/tela/sheet/sheet.client.vue +150 -0
  206. package/components/tela/sheet/sheet.mdx +176 -0
  207. package/components/tela/sheet/sheet.stories.ts +201 -0
  208. package/components/tela/sheet/variants.ts +22 -0
  209. package/components/tela/side-sheet/side-sheet.mdx +131 -0
  210. package/components/tela/side-sheet/side-sheet.stories.ts +134 -0
  211. package/components/tela/side-sheet/side-sheet.vue +106 -0
  212. package/components/tela/skeleton/skeleton.mdx +165 -0
  213. package/components/tela/skeleton/skeleton.stories.ts +35 -0
  214. package/components/tela/skeleton/skeleton.vue +45 -0
  215. package/components/tela/skeleton-icon.vue +24 -0
  216. package/components/tela/span.vue +24 -0
  217. package/components/tela/star-button.vue +70 -0
  218. package/components/tela/status/status-lean.vue +30 -0
  219. package/components/tela/status/status.mdx +187 -0
  220. package/components/tela/status/status.stories.ts +160 -0
  221. package/components/tela/status/status.vue +420 -0
  222. package/components/tela/status-bar/status-bar.mdx +178 -0
  223. package/components/tela/status-bar/status-bar.stories.ts +64 -0
  224. package/components/tela/status-bar/status-bar.vue +56 -0
  225. package/components/tela/status-bar/types.ts +5 -0
  226. package/components/tela/switch/switch.mdx +118 -0
  227. package/components/tela/switch/switch.stories.ts +80 -0
  228. package/components/tela/switch/switch.vue +56 -0
  229. package/components/tela/table/table-body.vue +13 -0
  230. package/components/tela/table/table-caption.vue +13 -0
  231. package/components/tela/table/table-cell.vue +20 -0
  232. package/components/tela/table/table-empty.vue +37 -0
  233. package/components/tela/table/table-footer.vue +13 -0
  234. package/components/tela/table/table-head.vue +13 -0
  235. package/components/tela/table/table-header.vue +13 -0
  236. package/components/tela/table/table-row.vue +13 -0
  237. package/components/tela/table/table.mdx +230 -0
  238. package/components/tela/table/table.stories.ts +384 -0
  239. package/components/tela/table/table.vue +15 -0
  240. package/components/tela/tabs/tabs-content.vue +20 -0
  241. package/components/tela/tabs/tabs-indicator.vue +22 -0
  242. package/components/tela/tabs/tabs-list.vue +23 -0
  243. package/components/tela/tabs/tabs-root.vue +15 -0
  244. package/components/tela/tabs/tabs-trigger.vue +27 -0
  245. package/components/tela/tabs/tabs.mdx +138 -0
  246. package/components/tela/tabs/tabs.stories.ts +72 -0
  247. package/components/tela/tabs/tabs.vue +61 -0
  248. package/components/tela/tags/tags-select.mdx +318 -0
  249. package/components/tela/tags/tags-select.stories.ts +47 -0
  250. package/components/tela/tags/tags-select.vue +637 -0
  251. package/components/tela/tags/tags.mdx +151 -0
  252. package/components/tela/tags/tags.stories.ts +118 -0
  253. package/components/tela/tags/tags.vue +112 -0
  254. package/components/tela/textarea/textarea.mdx +102 -0
  255. package/components/tela/textarea/textarea.stories.ts +50 -0
  256. package/components/tela/textarea/textarea.vue +34 -0
  257. package/components/tela/toggle-group.vue +91 -0
  258. package/components/tela/tooltip/tooltip-content.vue +45 -0
  259. package/components/tela/tooltip/tooltip-provider.vue +12 -0
  260. package/components/tela/tooltip/tooltip-root.vue +15 -0
  261. package/components/tela/tooltip/tooltip-trigger.vue +12 -0
  262. package/components/tela/tooltip/tooltip.mdx +196 -0
  263. package/components/tela/tooltip/tooltip.stories.ts +200 -0
  264. package/components/tela/tooltip/tooltip.vue +91 -0
  265. package/components/tela/tooltip-group/tooltip-group-trigger.vue +92 -0
  266. package/components/tela/tooltip-group/tooltip-group.mdx +236 -0
  267. package/components/tela/tooltip-group/tooltip-group.stories.ts +465 -0
  268. package/components/tela/tooltip-group/tooltip-group.vue +35 -0
  269. package/components/tela/transparent-input.vue +151 -0
  270. package/components/tela/variable-icon.vue +28 -0
  271. package/components/tela/variable-input.vue +77 -0
  272. package/components/tela/wide-button/wide-button.vue +40 -0
  273. package/components.json +18 -0
  274. package/composables/status-toast.ts +67 -0
  275. package/css/reset.css +386 -0
  276. package/css/text.css +22 -0
  277. package/lib/doc-generator.ts +903 -0
  278. package/lib/extractors/volar-extract.ts +186 -0
  279. package/lib/type-resolver.ts +402 -0
  280. package/lib/utils.ts +6 -0
  281. package/modules/tela-build-docs/index.ts +139 -0
  282. package/nuxt.config.ts +80 -0
  283. package/package.json +84 -0
  284. package/plugins/test-id.ts +7 -0
  285. package/tsconfig.json +7 -0
  286. package/types/custom-icon.ts +1 -0
  287. package/types/index.ts +2 -0
  288. package/types/status.ts +1 -0
  289. package/unocss.config.ts +89 -0
  290. package/utils/component-utils.ts +30 -0
  291. package/utils/design-tokens.ts +431 -0
  292. package/utils/fold.ts +8 -0
  293. package/utils/select-menu.ts +10 -0
  294. package/utils/status.ts +1 -0
  295. package/utils/without-keys.ts +34 -0
@@ -0,0 +1,326 @@
1
+ <script setup lang="ts">
2
+ import { ref, watch, onMounted, onUnmounted, nextTick, defineProps, defineEmits } from 'vue'
3
+ import { useNotification } from '../../../../application/composables/notification'
4
+ import type { Column, Row, ComplexTableType } from './types'
5
+ import { HoverMechanismTypes } from './types'
6
+ import TelaTableHeader from './complex-table-header.vue'
7
+ import TelaTableRow from './complex-table-row.vue'
8
+ import { useTableSelection } from './composables/table-selection'
9
+ import { useTableCommon } from './composables/table-common'
10
+ import { useVirtualScroll } from './composables/virtual-scroll'
11
+ import './styles/table-shared.css'
12
+
13
+ const props = defineProps<ComplexTableType>()
14
+
15
+ const emit = defineEmits<{
16
+ (event: 'select', ids: string[], isAll: boolean): void
17
+ (event: 'loadMore'): void
18
+ (event: 'open', id: string): void
19
+ (event: 'scroll', scrollEvent: Event): void
20
+ }>()
21
+
22
+ const notification = useNotification()
23
+ const slots = useSlots()
24
+
25
+ const mainTableEl = ref<HTMLElement>()
26
+ const containerEl = ref<HTMLElement>()
27
+
28
+ // Use table composables
29
+ const {
30
+ selectedRows: internalSelectedRows,
31
+ isAllSelected,
32
+ selectAll,
33
+ toggleRow,
34
+ } = useTableSelection({
35
+ rows: computed(() => props.rows),
36
+ initialSelectedRows: props.selectedRows,
37
+ })
38
+
39
+ const selectedRows = computed(() => props.selectedRows ?? internalSelectedRows.value)
40
+
41
+ const {
42
+ hoveredRowIndex,
43
+ hasHorizontalScroll,
44
+ handleRowSelect,
45
+ handleSelectAllRows,
46
+ handleScroll,
47
+ updateScrollButtonsState,
48
+ handleRowHover,
49
+ } = useTableCommon({
50
+ props,
51
+ emit,
52
+ mainTableEl,
53
+ selectedRows,
54
+ isAllSelected,
55
+ selectAll,
56
+ toggleRow,
57
+ })
58
+
59
+ const hoverMechanism = computed(() => props.hoverMechanism || HoverMechanismTypes.Cell)
60
+ const columnsWithHeaders: ComputedRef<Column[]> = computed(() => props.columns.filter((column: Column) => slots[`header-${column.key}`] !== undefined))
61
+ const shouldHideScrollbar = computed(() => props.hideScrollbar || !hasHorizontalScroll.value)
62
+
63
+ // Track if we're currently loading more data to prevent duplicate requests
64
+ const isLoadingMore = ref(false)
65
+
66
+ // Virtual scroll setup
67
+ const virtualScroll = props.useVirtualization !== false
68
+ ? useVirtualScroll({
69
+ count: computed(() => props.rows.length),
70
+ getScrollElement: () => mainTableEl.value || null,
71
+ estimateSize: props.virtualizedRowHeight ?? 64,
72
+ overscan: props.virtualizedOverscan ?? 5,
73
+ initialBatchSize: props.virtualizedInitialBatchSize ?? 30,
74
+ })
75
+ : null
76
+
77
+ const displayRows = computed(() => {
78
+ if (!virtualScroll) {
79
+ return props.rows.map((row, index) => ({ row, index }))
80
+ }
81
+
82
+ return virtualScroll.virtualItems.value.map(virtualItem => ({
83
+ row: props.rows[virtualItem.index] || {} as Row,
84
+ index: virtualItem.index,
85
+ }))
86
+ })
87
+
88
+ const topSpacerHeight = computed(() => {
89
+ if (!virtualScroll)
90
+ return 0
91
+ const startIndex = virtualScroll.range.value.startIndex
92
+ return startIndex * (props.virtualizedRowHeight ?? 64)
93
+ })
94
+
95
+ const bottomSpacerHeight = computed(() => {
96
+ if (!virtualScroll)
97
+ return 0
98
+ const endIndex = virtualScroll.range.value.endIndex
99
+ return (props.rows.length - endIndex - 1) * (props.virtualizedRowHeight ?? 64)
100
+ })
101
+
102
+ const shouldShowTopSpacer = computed(() => {
103
+ return virtualScroll && topSpacerHeight.value > 0
104
+ })
105
+
106
+ const shouldShowBottomSpacer = computed(() => {
107
+ return virtualScroll && bottomSpacerHeight.value > 0
108
+ })
109
+
110
+ // Infinite scroll with virtualization support
111
+ let lastLoadMoreTriggeredAt = 0
112
+ watch(() => virtualScroll?.range.value, async (newRange) => {
113
+ if (!newRange || !virtualScroll || isLoadingMore.value)
114
+ return
115
+ // Check if we're near the end and haven't recently triggered a load
116
+ const now = Date.now()
117
+ if (newRange.endIndex >= props.rows.length - 10 && now - lastLoadMoreTriggeredAt > 500) {
118
+ try {
119
+ isLoadingMore.value = true
120
+ lastLoadMoreTriggeredAt = now
121
+ emit('loadMore')
122
+ // Give time for new data to be added before allowing another load
123
+ await nextTick()
124
+ setTimeout(() => {
125
+ isLoadingMore.value = false
126
+ }, 300)
127
+ }
128
+ catch (error) {
129
+ notification.error('Load More Failed', (error as Error).message)
130
+ isLoadingMore.value = false
131
+ }
132
+ }
133
+ }, { deep: true })
134
+
135
+ // Fallback for non-virtualized mode
136
+ if (!virtualScroll) {
137
+ useInfiniteScroll(mainTableEl, () => {
138
+ try {
139
+ emit('loadMore')
140
+ }
141
+ catch (error) {
142
+ notification.error('Load More Failed', (error as Error).message)
143
+ }
144
+ }, { distance: 100, direction: 'bottom' })
145
+ }
146
+
147
+ onMounted(() => {
148
+ updateScrollButtonsState()
149
+ window.addEventListener('resize', updateScrollButtonsState)
150
+ virtualScroll?.init()
151
+ })
152
+
153
+ onUnmounted(() => {
154
+ window.removeEventListener('resize', updateScrollButtonsState)
155
+ virtualScroll?.destroy()
156
+ })
157
+
158
+ watch(() => props.columns, () => {
159
+ nextTick(updateScrollButtonsState)
160
+ }, { deep: true })
161
+
162
+ const hasRowIndex = computed(() => props.allowRowIndex && props.rows.length)
163
+ const hasSelect = computed(() => props.allowSelect && props.rows.length)
164
+
165
+ watch([hasRowIndex, hasSelect, mainTableEl], () => {
166
+ if (!mainTableEl.value)
167
+ return
168
+
169
+ const el = mainTableEl.value
170
+ el.classList.remove('has-index', 'has-select', 'has-index-and-select')
171
+
172
+ if (hasRowIndex.value && hasSelect.value) {
173
+ el.classList.add('has-index-and-select')
174
+ }
175
+ else if (hasRowIndex.value) {
176
+ el.classList.add('has-index')
177
+ }
178
+ else if (hasSelect.value) {
179
+ el.classList.add('has-select')
180
+ }
181
+ }, { immediate: true })
182
+ </script>
183
+
184
+ <template>
185
+ <div
186
+ ref="containerEl"
187
+ rounded-12px flex="~"
188
+ b="0.5px gray-300"
189
+ overflow-hidden
190
+ :style="{
191
+ width: '100%',
192
+ boxShadow: '0px 2px 5px 0px #677F940D, 0px 3px 16px 0px #677F940A',
193
+ }"
194
+ >
195
+ <div
196
+ ref="mainTableEl"
197
+ w-full
198
+ class="main-table-container overflow-auto"
199
+ relative
200
+ :class="{ 'hide-scrollbar': shouldHideScrollbar }"
201
+ :style="{ maxHeight: `${props.maxHeight || 462.5}px` }"
202
+ @scroll="handleScroll"
203
+ >
204
+ <table z-2 class="relative w-full min-w-full">
205
+ <TelaTableHeader
206
+ :columns="columns"
207
+ :rows="rows"
208
+ :selected-rows="selectedRows"
209
+ :allow-select="allowSelect"
210
+ :allow-row-index="allowRowIndex"
211
+ :allow-row-action="$slots['row-action'] !== undefined"
212
+ :header-class="props.headerClass"
213
+ @select-all="handleSelectAllRows"
214
+ >
215
+ <template #leading-header>
216
+ <slot name="leading-header" />
217
+ </template>
218
+
219
+ <template
220
+ v-for="column in columnsWithHeaders"
221
+ :key="column.key"
222
+ #[`header-${column.key}`]
223
+ >
224
+ <slot :name="`header-${column.key}`" :column="column" />
225
+ </template>
226
+ </TelaTableHeader>
227
+
228
+ <tbody v-if="rows.length">
229
+ <tr v-if="shouldShowTopSpacer" :style="{ height: `${topSpacerHeight}px` }">
230
+ <td :colspan="columns.length + (allowSelect ? 1 : 0) + (allowRowIndex ? 1 : 0) + 2" />
231
+ </tr>
232
+
233
+ <TelaTableRow
234
+ v-for="{ row: rowData, index: idx } in displayRows"
235
+ :key="rowData.id || idx"
236
+ :row="rowData"
237
+ :row-index="idx"
238
+ :columns="columns"
239
+ :allow-select="allowSelect"
240
+ :allow-row-index="allowRowIndex"
241
+ :allow-row-action="$slots['row-action'] !== undefined"
242
+ :is-selected="selectedRows?.includes(rowData.id as string)"
243
+ :loading="props.loading"
244
+ :disable-row-click="props.disableRowClick?.(rowData)"
245
+ :hover-mechanism="hoverMechanism"
246
+ :hovered-row-index="hoveredRowIndex"
247
+ :row-class="props.rowClass"
248
+ :row-index-class="props.rowIndexClass"
249
+ :row-title-path="props.rowTitlePath"
250
+ :row-index-path="props.rowIndexPath"
251
+ :columns-class="props.columnsClass"
252
+ :is-first-row="idx === 0"
253
+ :is-last-row="idx === rows.length - 1"
254
+ :hide-error="props.hideError"
255
+ :total-rows="rows.length"
256
+ @select="handleRowSelect"
257
+ @open="(id: string) => emit('open', id)"
258
+ @hover="handleRowHover"
259
+ >
260
+ <template #leading="slotProps">
261
+ <slot name="leading" v-bind="slotProps" />
262
+ </template>
263
+
264
+ <template v-for="column in columns" :key="column.key" #[column.key!]="slotProps">
265
+ <slot v-if="column.key" :name="column.key" v-bind="slotProps" />
266
+ </template>
267
+
268
+ <template v-if="$slots['row-action']" #row-action="slotProps">
269
+ <slot name="row-action" v-bind="slotProps" />
270
+ </template>
271
+ </TelaTableRow>
272
+
273
+ <tr v-if="shouldShowBottomSpacer" :style="{ height: `${bottomSpacerHeight}px` }">
274
+ <td :colspan="columns.length + (allowSelect ? 1 : 0) + (allowRowIndex ? 1 : 0) + 2" />
275
+ </tr>
276
+
277
+ <tr v-if="$slots.footer && !virtualScroll" b="b-0.5px gray-200">
278
+ <td :colspan="columns.length + 2">
279
+ <div flex items-center h-64px b="t-0.5px gray-200" :class="props.rowClass">
280
+ <slot name="footer" />
281
+ </div>
282
+ </td>
283
+ <td sticky right-0px z-1 colspan="1">
284
+ <div
285
+ flex="~" bg-white w-86px h-64px items-center justify-center h-full px-12px
286
+ b="t-0.5px l-0.5px gray-200"
287
+ class="shadow" :class="props.rowClass"
288
+ />
289
+ </td>
290
+ </tr>
291
+ </tbody>
292
+ <tbody v-else>
293
+ <tr>
294
+ <td colspan="100%">
295
+ <div h-64px flex items-center justify-center w-full relative>
296
+ <div class="empty-state-centered">
297
+ <slot name="empty-state" />
298
+ </div>
299
+ </div>
300
+ </td>
301
+ </tr>
302
+ </tbody>
303
+ </table>
304
+ </div>
305
+ </div>
306
+ </template>
307
+
308
+ <style scoped>
309
+ /* Virtualization-specific styles for sticky headers */
310
+ thead th.sticky {
311
+ position: sticky !important;
312
+ z-index: 30 !important;
313
+ }
314
+
315
+ thead th.z-30 {
316
+ z-index: 30 !important;
317
+ }
318
+
319
+ thead th[style*="left"] {
320
+ position: sticky !important;
321
+ }
322
+
323
+ thead th.bg-white {
324
+ background-color: white !important;
325
+ }
326
+ </style>
@@ -0,0 +1,358 @@
1
+ /* eslint-disable no-console */
2
+ import type { Meta, StoryObj } from '@storybook/vue3'
3
+ import TelaComplexTable from './complex-table.vue'
4
+ import { HoverMechanismTypes } from './types'
5
+
6
+ const meta = {
7
+ title: 'Components/Tela/ComplexTable',
8
+ component: TelaComplexTable,
9
+ tags: ['autodocs'],
10
+ parameters: {
11
+ layout: 'centered',
12
+ docs: {
13
+ description: {
14
+ component: 'A complex table component with advanced features including column management, sorting, row selection, status indicators, custom cell rendering, and hover interactions. Supports virtualized rendering for large datasets and provides flexible column configuration. Perfect for data-heavy applications requiring sophisticated table functionality.',
15
+ },
16
+ },
17
+ },
18
+ argTypes: {
19
+ columns: {
20
+ control: 'object',
21
+ description: 'Array of column definitions. Each column should have `title`, `key`, and optionally `isDefault` properties.',
22
+ },
23
+ rows: {
24
+ control: 'object',
25
+ description: 'Array of row data objects. Each row should have `id`, `index`, `status`, `name`, and `data` (object with column keys) properties.',
26
+ },
27
+ hoverMechanism: {
28
+ control: 'select',
29
+ options: Object.values(HoverMechanismTypes),
30
+ description: 'Mechanism for handling row hover interactions.',
31
+ },
32
+ },
33
+ } satisfies Meta<typeof TelaComplexTable>
34
+
35
+ export default meta
36
+ type Story = StoryObj<typeof meta>
37
+
38
+ const mockColumns = [
39
+ { title: 'Column 1', key: 'col1', isDefault: false },
40
+ { title: 'Column 2', key: 'col2', isDefault: false },
41
+ { title: 'Column 3', key: 'col3', isDefault: true },
42
+ ]
43
+
44
+ const mockRows = [
45
+ {
46
+ id: '1',
47
+ index: 1,
48
+ status: 'success',
49
+ name: 'Row 1',
50
+ data: {
51
+ 'Column 1': 'Data 1-1',
52
+ 'Column 2': 'Data 1-2',
53
+ 'Column 3': 'Data 1-3',
54
+ },
55
+ },
56
+ {
57
+ id: '2',
58
+ index: 2,
59
+ status: 'success',
60
+ name: 'Row 2',
61
+ data: {
62
+ 'Column 1': 'Data 2-1',
63
+ 'Column 2': 'Data 2-2',
64
+ 'Column 3': 'Data 2-3',
65
+ },
66
+ },
67
+ ]
68
+
69
+ const CustomHeaderCell = {
70
+ props: ['column', 'sortDirection'],
71
+ template: `
72
+ <div class="flex items-center gap-2 px-4 py-2">
73
+ <span class="font-medium">{{ column.title }}</span>
74
+ <div class="flex flex-col">
75
+ <button
76
+ class="w-0 h-0 border-l-4 border-r-4 border-b-4 border-l-transparent border-r-transparent"
77
+ :class="sortDirection === 'asc' ? 'border-blue-600' : 'border-gray-300'"
78
+ @click="$emit('sort', 'asc')"
79
+ />
80
+ <button
81
+ class="w-0 h-0 border-l-4 border-r-4 border-t-4 border-l-transparent border-r-transparent mt-1"
82
+ :class="sortDirection === 'desc' ? 'border-blue-600' : 'border-gray-300'"
83
+ @click="$emit('sort', 'desc')"
84
+ />
85
+ </div>
86
+ </div>
87
+ `,
88
+ emits: ['sort'],
89
+ }
90
+
91
+ export const Default: Story = {
92
+ args: {
93
+ columns: mockColumns,
94
+ rows: mockRows,
95
+ rowTitlePath: 'data.Column 1',
96
+ rowIndexPath: 'index',
97
+ allowSelect: false,
98
+ allowRowIndex: true,
99
+ loading: false,
100
+ maxHeight: 462.5,
101
+ },
102
+ }
103
+
104
+ export const WithCustomMaxHeight: Story = {
105
+ args: {
106
+ ...Default.args,
107
+ maxHeight: 300,
108
+ },
109
+ }
110
+
111
+ export const WithCustomHeaders: Story = {
112
+ args: {
113
+ ...Default.args,
114
+ },
115
+ render: args => ({
116
+ components: { TelaComplexTable, CustomHeaderCell },
117
+ setup() {
118
+ return {
119
+ args,
120
+ onSort: (column: string, direction: 'asc' | 'desc') => {
121
+ console.log(`Sorting ${column} ${direction}`)
122
+ },
123
+ }
124
+ },
125
+ template: `
126
+ <TelaComplexTable v-bind="args">
127
+ <template #header-col1="{ column }">
128
+ <CustomHeaderCell
129
+ :column="column"
130
+ sort-direction="asc"
131
+ @sort="direction => onSort(column.key, direction)"
132
+ />
133
+ </template>
134
+ <template #leading-header>
135
+ <div class="flex items-center px-4 h-56px w-full text-12-semibold">
136
+ Leading Header
137
+ </div>
138
+ </template>
139
+ </TelaComplexTable>
140
+ `,
141
+ }),
142
+ }
143
+
144
+ export const WithRowHover: Story = {
145
+ args: {
146
+ ...Default.args,
147
+ allowSelect: true,
148
+ allowRowIndex: true,
149
+ hoverMechanism: HoverMechanismTypes.Row,
150
+ },
151
+ render: args => ({
152
+ components: { TelaComplexTable },
153
+ setup() {
154
+ return {
155
+ args,
156
+ onHover: (rowData: any) => console.log('Hovering row:', rowData),
157
+ }
158
+ },
159
+ template: `
160
+ <TelaComplexTable
161
+ v-bind="args"
162
+ @hover="onHover"
163
+ />
164
+ `,
165
+ }),
166
+ }
167
+
168
+ export const WithCustomCellContent: Story = {
169
+ args: {
170
+ ...Default.args,
171
+ },
172
+ render: args => ({
173
+ components: { TelaComplexTable },
174
+ setup() {
175
+ return { args }
176
+ },
177
+ template: `
178
+ <TelaComplexTable v-bind="args">
179
+ <template #col1="{ rowData }">
180
+ <div class="px-4 py-2 text-blue-600">
181
+ Custom Cell: {{ rowData.data['Column 1'] }}
182
+ </div>
183
+ </template>
184
+ </TelaComplexTable>
185
+ `,
186
+ }),
187
+ }
188
+
189
+ export const WithLeadingContent: Story = {
190
+ args: {
191
+ ...Default.args,
192
+ },
193
+ render: args => ({
194
+ components: { TelaComplexTable },
195
+ setup() {
196
+ return { args }
197
+ },
198
+ template: `
199
+ <TelaComplexTable v-bind="args">
200
+ <template #leading="{ rowData, isLoading, rowTitle }">
201
+ <td>
202
+ <div class="px-4 py-2">
203
+ {{ isLoading ? 'Loading...' : rowTitle }}
204
+ </div>
205
+ </td>
206
+ </template>
207
+ </TelaComplexTable>
208
+ `,
209
+ }),
210
+ }
211
+
212
+ export const LoadingState: Story = {
213
+ args: {
214
+ ...Default.args,
215
+ rows: [
216
+ {
217
+ id: '1',
218
+ index: 1,
219
+ status: 'loading',
220
+ name: 'Row 1',
221
+ data: {},
222
+ },
223
+ ...mockRows.slice(1),
224
+ ],
225
+ },
226
+ }
227
+
228
+ export const WithError: Story = {
229
+ args: {
230
+ ...Default.args,
231
+ rows: [
232
+ {
233
+ id: '1',
234
+ index: 1,
235
+ status: 'failed',
236
+ name: 'Row 1',
237
+ errorMessage: 'Something went wrong loading this row',
238
+ data: {},
239
+ },
240
+ ...mockRows.slice(1),
241
+ ],
242
+ },
243
+ }
244
+
245
+ export const WithInfiniteScroll: Story = {
246
+ args: {
247
+ ...Default.args,
248
+ },
249
+ render: args => ({
250
+ components: { TelaComplexTable },
251
+ setup() {
252
+ return {
253
+ args,
254
+ onLoadMore: () => console.log('Loading more items...'),
255
+ }
256
+ },
257
+ template: `
258
+ <TelaComplexTable
259
+ v-bind="args"
260
+ @load-more="onLoadMore"
261
+ >
262
+ <template #footer>
263
+ <div class="px-4 text-gray-500">
264
+ Loading more items...
265
+ </div>
266
+ </template>
267
+ </TelaComplexTable>
268
+ `,
269
+ }),
270
+ }
271
+
272
+ export const WithRowOpenAction: Story = {
273
+ args: {
274
+ ...Default.args,
275
+ },
276
+ render: args => ({
277
+ components: { TelaComplexTable },
278
+ setup() {
279
+ return {
280
+ args,
281
+ onOpen: (id: string) => console.log('Opening row:', id),
282
+ }
283
+ },
284
+ template: `
285
+ <TelaComplexTable
286
+ v-bind="args"
287
+ @open="onOpen"
288
+ />
289
+ `,
290
+ }),
291
+ }
292
+
293
+ export const WithRowAction: Story = {
294
+ args: {
295
+ ...Default.args,
296
+ },
297
+ render: args => ({
298
+ components: { TelaComplexTable },
299
+ setup() {
300
+ return { args }
301
+ },
302
+ template: `
303
+ <TelaComplexTable v-bind="args">
304
+ <template #row-action="{ row }">
305
+ <button
306
+ class="px-3 py-1 bg-blue-500 text-white rounded hover:bg-blue-600"
307
+ @click="() => console.log('Action on row:', row.id)"
308
+ >
309
+ Action
310
+ </button>
311
+ </template>
312
+ </TelaComplexTable>
313
+ `,
314
+ }),
315
+ }
316
+
317
+ export const Empty: Story = {
318
+ args: {
319
+ ...Default.args,
320
+ rows: [],
321
+ },
322
+ render: args => ({
323
+ components: { TelaComplexTable },
324
+ setup() {
325
+ return { args }
326
+ },
327
+ template: `
328
+ <TelaComplexTable v-bind="args">
329
+ <template #empty-state>
330
+ <div class="text-gray-500">
331
+ No data available
332
+ </div>
333
+ </template>
334
+ </TelaComplexTable>
335
+ `,
336
+ }),
337
+ }
338
+
339
+ export const WithLastItem: Story = {
340
+ args: {
341
+ ...Default.args,
342
+ },
343
+ render: args => ({
344
+ components: { TelaComplexTable },
345
+ setup() {
346
+ return { args }
347
+ },
348
+ template: `
349
+ <TelaComplexTable v-bind="args">
350
+ <template #footer>
351
+ <div class="px-4 text-gray-500">
352
+ End of list
353
+ </div>
354
+ </template>
355
+ </TelaComplexTable>
356
+ `,
357
+ }),
358
+ }