@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,237 @@
1
+ <script setup lang="ts">
2
+ import { ref, watch, onMounted, nextTick, defineProps, defineEmits, defineAsyncComponent } from 'vue'
3
+ import { useNotification } from '../../../../application/composables/notification'
4
+ import type { Column, 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 './styles/table-shared.css'
11
+
12
+ const props = defineProps<ComplexTableType>()
13
+
14
+ const emit = defineEmits<{
15
+ (event: 'select', ids: string[], isAll: boolean): void
16
+ (event: 'loadMore'): void
17
+ (event: 'open', id: string): void
18
+ (event: 'scroll', scrollEvent: Event): void
19
+ }>()
20
+
21
+ // Lazy load virtualized version for better performance
22
+ const TelaComplexTableVirtualized = defineAsyncComponent(() => import('./complex-table-virtualized.vue'))
23
+
24
+ // Auto-enable virtualization for large datasets
25
+ const shouldUseVirtualization = computed(() => {
26
+ return props.useVirtualization === true
27
+ })
28
+
29
+ const notification = useNotification()
30
+ const slots = useSlots()
31
+
32
+ const mainTableEl = ref<HTMLElement>()
33
+ const containerEl = ref<HTMLElement>()
34
+
35
+ // Use table composables
36
+ const {
37
+ selectedRows: internalSelectedRows,
38
+ isAllSelected,
39
+ selectAll,
40
+ toggleRow,
41
+ } = useTableSelection({
42
+ rows: computed(() => props.rows),
43
+ initialSelectedRows: props.selectedRows,
44
+ })
45
+
46
+ const selectedRows = computed(() => props.selectedRows ?? internalSelectedRows.value)
47
+
48
+ const {
49
+ hoveredRowIndex,
50
+ hasHorizontalScroll,
51
+ handleRowSelect,
52
+ handleSelectAllRows,
53
+ handleScroll,
54
+ updateScrollButtonsState,
55
+ handleRowHover,
56
+ } = useTableCommon({
57
+ props,
58
+ emit,
59
+ mainTableEl,
60
+ selectedRows,
61
+ isAllSelected,
62
+ selectAll,
63
+ toggleRow,
64
+ })
65
+
66
+ const hoverMechanism = computed(() => props.hoverMechanism || HoverMechanismTypes.Cell)
67
+ const columnsWithHeaders: ComputedRef<Column[]> = computed(() => props.columns.filter((column: Column) => slots[`header-${column.key}`] !== undefined))
68
+ const shouldHideScrollbar = computed(() => props.hideScrollbar || !hasHorizontalScroll.value)
69
+
70
+ useInfiniteScroll(mainTableEl, () => {
71
+ try {
72
+ emit('loadMore')
73
+ }
74
+ catch (error) {
75
+ notification.error('Load More Failed', (error as Error).message)
76
+ }
77
+ }, { distance: 100, direction: 'bottom' })
78
+
79
+ onMounted(() => {
80
+ updateScrollButtonsState()
81
+ window.addEventListener('resize', updateScrollButtonsState)
82
+ })
83
+
84
+ watch(() => props.columns, () => {
85
+ nextTick(updateScrollButtonsState)
86
+ }, { deep: true })
87
+
88
+ const hasRowIndex = computed(() => props.allowRowIndex && props.rows.length)
89
+ const hasSelect = computed(() => props.allowSelect && props.rows.length)
90
+
91
+ watch([hasRowIndex, hasSelect, mainTableEl], () => {
92
+ if (!mainTableEl.value)
93
+ return
94
+
95
+ const el = mainTableEl.value
96
+ el.classList.remove('has-index', 'has-select', 'has-index-and-select')
97
+
98
+ if (hasRowIndex.value && hasSelect.value) {
99
+ el.classList.add('has-index-and-select')
100
+ }
101
+ else if (hasRowIndex.value) {
102
+ el.classList.add('has-index')
103
+ }
104
+ else if (hasSelect.value) {
105
+ el.classList.add('has-select')
106
+ }
107
+ }, { immediate: true })
108
+ </script>
109
+
110
+ <template>
111
+ <!-- Use virtualized version for large datasets -->
112
+ <TelaComplexTableVirtualized
113
+ v-if="shouldUseVirtualization"
114
+ v-bind="props"
115
+ @select="(ids: string[], isAll: boolean) => emit('select', ids, isAll)"
116
+ @load-more="emit('loadMore')"
117
+ @open="(id: string) => emit('open', id)"
118
+ @scroll="(e: Event) => emit('scroll', e)"
119
+ >
120
+ <template v-for="(_, slot) in $slots" #[slot]="slotProps">
121
+ <slot :name="slot" v-bind="slotProps" />
122
+ </template>
123
+ </TelaComplexTableVirtualized>
124
+
125
+ <!-- Non-virtualized version for small datasets -->
126
+ <div
127
+ v-else
128
+ ref="containerEl"
129
+ rounded-12px flex="~"
130
+ b="0.5px gray-300"
131
+ overflow-hidden
132
+ :style="{
133
+ width: '100%',
134
+ boxShadow: '0px 2px 5px 0px #677F940D, 0px 3px 16px 0px #677F940A',
135
+ }"
136
+ >
137
+ <div
138
+ ref="mainTableEl" w-full class="main-table-container" relative
139
+ :class="{ 'hide-scrollbar': shouldHideScrollbar, 'overflow-x-scroll': rows.length > 0 }"
140
+ :style="{ maxHeight: `${props.maxHeight || 462.5}px` }"
141
+ @scroll="handleScroll"
142
+ >
143
+ <table z-2>
144
+ <TelaTableHeader
145
+ :columns="columns"
146
+ :rows="rows"
147
+ :selected-rows="selectedRows"
148
+ :allow-select="allowSelect"
149
+ :allow-row-index="allowRowIndex"
150
+ :allow-row-action="$slots['row-action'] !== undefined"
151
+ :header-class="props.headerClass"
152
+ @select-all="handleSelectAllRows"
153
+ >
154
+ <template #leading-header>
155
+ <slot name="leading-header" />
156
+ </template>
157
+
158
+ <template
159
+ v-for="column in columnsWithHeaders"
160
+ :key="column.key"
161
+ #[`header-${column.key}`]
162
+ >
163
+ <slot :name="`header-${column.key}`" :column="column" />
164
+ </template>
165
+ </TelaTableHeader>
166
+
167
+ <tbody v-if="rows.length">
168
+ <TelaTableRow
169
+ v-for="(rowData, idx) in rows"
170
+ :key="idx"
171
+ :row="rowData"
172
+ :row-index="idx"
173
+ :columns="columns"
174
+ :allow-select="allowSelect"
175
+ :allow-row-index="allowRowIndex"
176
+ :allow-row-action="$slots['row-action'] !== undefined"
177
+ :is-selected="selectedRows?.includes(rowData.id as string)"
178
+ :loading="props.loading"
179
+ :disable-row-click="props.disableRowClick?.(rowData)"
180
+ :hover-mechanism="hoverMechanism"
181
+ :hovered-row-index="hoveredRowIndex"
182
+ :row-class="props.rowClass"
183
+ :row-index-class="props.rowIndexClass"
184
+ :row-title-path="props.rowTitlePath"
185
+ :row-index-path="props.rowIndexPath"
186
+ :columns-class="props.columnsClass"
187
+ :is-first-row="idx === 0"
188
+ :is-last-row="idx === rows.length - 1"
189
+ :hide-error="props.hideError"
190
+ :total-rows="rows.length"
191
+ @select="handleRowSelect"
192
+ @open="(id: string) => emit('open', id)"
193
+ @hover="handleRowHover"
194
+ >
195
+ <template #leading="slotProps">
196
+ <slot name="leading" v-bind="slotProps" />
197
+ </template>
198
+
199
+ <template v-for="column in columns" :key="column.key" #[column.key!]="slotProps">
200
+ <slot v-if="column.key" :name="column.key" v-bind="slotProps" />
201
+ </template>
202
+
203
+ <template v-if="$slots['row-action']" #row-action="slotProps">
204
+ <slot name="row-action" v-bind="slotProps" />
205
+ </template>
206
+ </TelaTableRow>
207
+
208
+ <tr v-if="$slots.footer" b="b-0.5px gray-200">
209
+ <td :colspan="columns.length + 2">
210
+ <div flex items-center h-64px b="t-0.5px gray-200" :class="props.rowClass">
211
+ <slot name="footer" />
212
+ </div>
213
+ </td>
214
+ <td sticky right-0px z-1 colspan="1">
215
+ <div
216
+ flex="~" bg-white w-86px h-64px items-center justify-center h-full px-12px
217
+ b="t-0.5px l-0.5px gray-200"
218
+ class="shadow" :class="props.rowClass"
219
+ />
220
+ </td>
221
+ </tr>
222
+ </tbody>
223
+ <tbody v-else>
224
+ <tr>
225
+ <td colspan="100%">
226
+ <div h-64px flex items-center justify-center w-full relative>
227
+ <div class="empty-state-centered">
228
+ <slot name="empty-state" />
229
+ </div>
230
+ </div>
231
+ </td>
232
+ </tr>
233
+ </tbody>
234
+ </table>
235
+ </div>
236
+ </div>
237
+ </template>
@@ -0,0 +1,93 @@
1
+ import type { Ref } from 'vue'
2
+ import { ref, watch } from 'vue'
3
+ import type { HoverMechanism, Row } from '../types'
4
+ import { HoverMechanismTypes } from '../types'
5
+
6
+ export interface UseTableCommonOptions {
7
+ props: {
8
+ hoverMechanism?: HoverMechanism
9
+ disableRowClick?: (row: Row) => boolean
10
+ selectedRows?: string[]
11
+ rows: Row[]
12
+ }
13
+ emit: {
14
+ (event: 'select', ids: string[], isAll: boolean): void
15
+ (event: 'scroll', scrollEvent: Event): void
16
+ }
17
+ mainTableEl: Ref<HTMLElement | undefined>
18
+ selectedRows: Ref<string[]>
19
+ isAllSelected: Ref<boolean>
20
+ selectAll: (value: boolean) => string[]
21
+ toggleRow: (id: string) => string[]
22
+ }
23
+
24
+ export function useTableCommon(options: UseTableCommonOptions) {
25
+ const { props, emit, mainTableEl, selectedRows, isAllSelected, selectAll, toggleRow } = options
26
+
27
+ const hoveredRowIndex = ref<number | null>(null)
28
+ const hasHorizontalScroll = ref(false)
29
+
30
+ function shouldEnableRowClick(rowData: Row) {
31
+ return props.hoverMechanism === HoverMechanismTypes.Row && !props.disableRowClick?.(rowData)
32
+ }
33
+
34
+ function handleRowSelect(rowId: string) {
35
+ if (props.selectedRows) {
36
+ const newSelection = selectedRows.value.includes(rowId)
37
+ ? selectedRows.value.filter((id: string) => id !== rowId)
38
+ : [...selectedRows.value, rowId]
39
+ emit('select', newSelection, newSelection.length === props.rows.length)
40
+ return
41
+ }
42
+
43
+ const newSelection = toggleRow(rowId)
44
+ emit('select', newSelection, isAllSelected.value)
45
+ }
46
+
47
+ function handleSelectAllRows(value: boolean) {
48
+ if (props.selectedRows) {
49
+ const newSelection = value ? props.rows.map((row: Row) => row?.id || '').filter(Boolean) : []
50
+ emit('select', newSelection, value)
51
+ return
52
+ }
53
+
54
+ const newSelection = selectAll(value)
55
+ emit('select', newSelection, value)
56
+ }
57
+
58
+ function handleScroll(scrollEvent: Event) {
59
+ emit('scroll', scrollEvent)
60
+ }
61
+
62
+ function updateScrollButtonsState() {
63
+ if (!mainTableEl.value)
64
+ return
65
+
66
+ const el = mainTableEl.value
67
+ const hasHorizontalScrollValue = el.scrollWidth > el.clientWidth
68
+ hasHorizontalScroll.value = hasHorizontalScrollValue
69
+ el.classList.toggle('has-scroll', hasHorizontalScrollValue)
70
+ }
71
+
72
+ function handleRowHover(index: number | null) {
73
+ hoveredRowIndex.value = index
74
+ }
75
+
76
+ // Watch for row changes to maintain selection state
77
+ watch(() => props.rows, () => {
78
+ if (isAllSelected.value && !props.selectedRows) {
79
+ selectAll(true)
80
+ }
81
+ }, { deep: true })
82
+
83
+ return {
84
+ hoveredRowIndex,
85
+ hasHorizontalScroll,
86
+ shouldEnableRowClick,
87
+ updateScrollButtonsState,
88
+ handleRowSelect,
89
+ handleSelectAllRows,
90
+ handleScroll,
91
+ handleRowHover,
92
+ }
93
+ }
@@ -0,0 +1,87 @@
1
+ import type { Ref, ComputedRef } from 'vue'
2
+ import { ref, computed } from 'vue'
3
+
4
+ export interface UseTableSelectionOptions {
5
+ rows: Ref<Array<{ id?: string }>> | Array<{ id?: string }>
6
+ initialSelectedRows?: string[]
7
+ }
8
+
9
+ export function useTableSelection(options: UseTableSelectionOptions) {
10
+ const rows = ref(options.rows)
11
+ const selectedRows = ref<string[]>(options.initialSelectedRows || [])
12
+
13
+ const selectableRows = computed(() => {
14
+ const rowsArray = Array.isArray(rows.value) ? rows.value : rows.value
15
+ return rowsArray.filter(row => row.id)
16
+ })
17
+
18
+ const isAllSelected = computed(() => {
19
+ if (selectableRows.value.length === 0)
20
+ return false
21
+ return selectedRows.value.length === selectableRows.value.length
22
+ })
23
+
24
+ const isSomeSelected = computed(() => {
25
+ return selectedRows.value.length > 0 && selectedRows.value.length < selectableRows.value.length
26
+ })
27
+
28
+ function selectAll(value: boolean) {
29
+ if (value) {
30
+ selectedRows.value = selectableRows.value.map(row => row.id!).filter(Boolean)
31
+ }
32
+ else {
33
+ selectedRows.value = []
34
+ }
35
+ return selectedRows.value
36
+ }
37
+
38
+ function toggleRow(id: string) {
39
+ const index = selectedRows.value.indexOf(id)
40
+ if (index > -1) {
41
+ selectedRows.value.splice(index, 1)
42
+ }
43
+ else {
44
+ selectedRows.value.push(id)
45
+ }
46
+ return selectedRows.value
47
+ }
48
+
49
+ function selectRow(id: string) {
50
+ if (!selectedRows.value.includes(id)) {
51
+ selectedRows.value.push(id)
52
+ }
53
+ }
54
+
55
+ function deselectRow(id: string) {
56
+ const index = selectedRows.value.indexOf(id)
57
+ if (index > -1) {
58
+ selectedRows.value.splice(index, 1)
59
+ }
60
+ }
61
+
62
+ function setSelectedRows(ids: string[]) {
63
+ selectedRows.value = ids
64
+ }
65
+
66
+ function clearSelection() {
67
+ selectedRows.value = []
68
+ }
69
+
70
+ function isRowSelected(id: string): boolean {
71
+ return selectedRows.value.includes(id)
72
+ }
73
+
74
+ return {
75
+ selectedRows,
76
+ isAllSelected,
77
+ isSomeSelected,
78
+ selectAll,
79
+ toggleRow,
80
+ selectRow,
81
+ deselectRow,
82
+ setSelectedRows,
83
+ clearSelection,
84
+ isRowSelected,
85
+ selectableRows: selectableRows as ComputedRef<Array<{ id?: string }>>,
86
+ }
87
+ }
@@ -0,0 +1,252 @@
1
+ import type { Ref } from 'vue'
2
+ import { computed, ref, watch, nextTick } from 'vue'
3
+
4
+ export interface VirtualItem {
5
+ index: number
6
+ start: number
7
+ end: number
8
+ size: number
9
+ key: string | number
10
+ }
11
+
12
+ export interface UseVirtualScrollOptions {
13
+ count: number | Ref<number>
14
+ getScrollElement: () => HTMLElement | null
15
+ estimateSize?: number | ((index: number) => number)
16
+ overscan?: number
17
+ horizontal?: boolean
18
+ paddingStart?: number
19
+ paddingEnd?: number
20
+ initialBatchSize?: number
21
+ }
22
+
23
+ export interface VirtualRange {
24
+ startIndex: number
25
+ endIndex: number
26
+ startOffset: number
27
+ endOffset: number
28
+ }
29
+
30
+ export function useVirtualScroll(options: UseVirtualScrollOptions) {
31
+ const count = ref(options.count)
32
+ const scrollElement = ref<HTMLElement | null>(null)
33
+ const scrollTop = ref(0)
34
+ const scrollLeft = ref(0)
35
+ const clientHeight = ref(0)
36
+ const clientWidth = ref(0)
37
+ const itemSizeCache = new Map<number, number>()
38
+
39
+ const estimateSize = options.estimateSize ?? 64
40
+ const overscan = options.overscan ?? 5
41
+ const horizontal = options.horizontal ?? false
42
+ const paddingStart = options.paddingStart ?? 0
43
+ const paddingEnd = options.paddingEnd ?? 0
44
+ const initialBatchSize = options.initialBatchSize ?? 30
45
+
46
+ const scrollOffset = computed(() => horizontal ? scrollLeft.value : scrollTop.value)
47
+ const viewportSize = computed(() => horizontal ? clientWidth.value : clientHeight.value)
48
+
49
+ function getItemSize(index: number): number {
50
+ if (itemSizeCache.has(index)) {
51
+ return itemSizeCache.get(index)!
52
+ }
53
+ const size = typeof estimateSize === 'function' ? estimateSize(index) : estimateSize
54
+ itemSizeCache.set(index, size)
55
+ return size
56
+ }
57
+
58
+ function getItemOffset(index: number): number {
59
+ let offset = paddingStart
60
+ for (let i = 0; i < index; i++) {
61
+ offset += getItemSize(i)
62
+ }
63
+ return offset
64
+ }
65
+
66
+ const getTotalSize = computed(() => {
67
+ let totalSize = paddingStart + paddingEnd
68
+ const itemCount = typeof count.value === 'number' ? count.value : count.value
69
+ for (let i = 0; i < itemCount; i++) {
70
+ totalSize += getItemSize(i)
71
+ }
72
+ return totalSize
73
+ })
74
+
75
+ const range = computed((): VirtualRange => {
76
+ const itemCount = typeof count.value === 'number' ? count.value : count.value
77
+ if (itemCount === 0) {
78
+ return {
79
+ startIndex: 0,
80
+ endIndex: 0,
81
+ startOffset: 0,
82
+ endOffset: 0,
83
+ }
84
+ }
85
+
86
+ // If viewport size is not yet calculated, render initial batch
87
+ if (viewportSize.value === 0) {
88
+ const endIndex = Math.min(initialBatchSize - 1, itemCount - 1)
89
+ return {
90
+ startIndex: 0,
91
+ endIndex,
92
+ startOffset: 0,
93
+ endOffset: getItemOffset(endIndex + 1),
94
+ }
95
+ }
96
+
97
+ let startIndex = 0
98
+ let accumulatedSize = paddingStart
99
+
100
+ // Find start index
101
+ for (let i = 0; i < itemCount; i++) {
102
+ const itemSize = getItemSize(i)
103
+ if (accumulatedSize + itemSize >= scrollOffset.value) {
104
+ startIndex = Math.max(0, i - overscan)
105
+ break
106
+ }
107
+ accumulatedSize += itemSize
108
+ }
109
+
110
+ // Find end index
111
+ let endIndex = startIndex
112
+ accumulatedSize = getItemOffset(startIndex)
113
+
114
+ for (let i = startIndex; i < itemCount; i++) {
115
+ if (accumulatedSize > scrollOffset.value + viewportSize.value) {
116
+ endIndex = i
117
+ break
118
+ }
119
+ accumulatedSize += getItemSize(i)
120
+ endIndex = i
121
+ }
122
+
123
+ // Apply overscan only once at the end
124
+ endIndex = Math.min(itemCount - 1, endIndex + overscan)
125
+
126
+ return {
127
+ startIndex,
128
+ endIndex,
129
+ startOffset: getItemOffset(startIndex),
130
+ endOffset: getItemOffset(Math.min(itemCount, endIndex + 1)),
131
+ }
132
+ })
133
+
134
+ const virtualItems = computed((): VirtualItem[] => {
135
+ const { startIndex, endIndex } = range.value
136
+ const items: VirtualItem[] = []
137
+
138
+ for (let i = startIndex; i <= endIndex; i++) {
139
+ const start = getItemOffset(i)
140
+ const size = getItemSize(i)
141
+ items.push({
142
+ index: i,
143
+ start,
144
+ end: start + size,
145
+ size,
146
+ key: i,
147
+ })
148
+ }
149
+
150
+ return items
151
+ })
152
+
153
+ function measureElement(index: number, element: HTMLElement) {
154
+ const size = horizontal
155
+ ? element.getBoundingClientRect().width
156
+ : element.getBoundingClientRect().height
157
+
158
+ const cachedSize = itemSizeCache.get(index)
159
+ if (cachedSize !== size) {
160
+ itemSizeCache.set(index, size)
161
+ }
162
+ }
163
+
164
+ function handleScroll() {
165
+ const el = options.getScrollElement()
166
+ if (!el)
167
+ return
168
+
169
+ scrollTop.value = el.scrollTop
170
+ scrollLeft.value = el.scrollLeft
171
+ clientHeight.value = el.clientHeight
172
+ clientWidth.value = el.clientWidth
173
+ }
174
+
175
+ function scrollToIndex(index: number, align: 'start' | 'center' | 'end' = 'start') {
176
+ const el = options.getScrollElement()
177
+ if (!el)
178
+ return
179
+
180
+ const offset = getItemOffset(index)
181
+ const size = getItemSize(index)
182
+
183
+ let scrollPosition = offset
184
+
185
+ if (align === 'center') {
186
+ scrollPosition = offset - (viewportSize.value - size) / 2
187
+ }
188
+ else if (align === 'end') {
189
+ scrollPosition = offset - viewportSize.value + size
190
+ }
191
+
192
+ if (horizontal) {
193
+ el.scrollLeft = scrollPosition
194
+ }
195
+ else {
196
+ el.scrollTop = scrollPosition
197
+ }
198
+ }
199
+
200
+ function scrollToOffset(offset: number) {
201
+ const el = options.getScrollElement()
202
+ if (!el)
203
+ return
204
+
205
+ if (horizontal) {
206
+ el.scrollLeft = offset
207
+ }
208
+ else {
209
+ el.scrollTop = offset
210
+ }
211
+ }
212
+
213
+ // Initialize and setup listeners
214
+ function init() {
215
+ scrollElement.value = options.getScrollElement()
216
+ if (!scrollElement.value)
217
+ return
218
+
219
+ handleScroll()
220
+ scrollElement.value.addEventListener('scroll', handleScroll, { passive: true })
221
+ }
222
+
223
+ function destroy() {
224
+ if (scrollElement.value) {
225
+ scrollElement.value.removeEventListener('scroll', handleScroll)
226
+ }
227
+ }
228
+
229
+ // Watch for count changes
230
+ watch(() => options.count, (newCount) => {
231
+ count.value = newCount
232
+ itemSizeCache.clear()
233
+ })
234
+
235
+ // Auto-init on mount
236
+ nextTick(() => {
237
+ init()
238
+ })
239
+
240
+ return {
241
+ virtualItems,
242
+ totalSize: getTotalSize,
243
+ range,
244
+ scrollToIndex,
245
+ scrollToOffset,
246
+ measureElement,
247
+ init,
248
+ destroy,
249
+ getVirtualItems: () => virtualItems.value,
250
+ getTotalSize: () => getTotalSize.value,
251
+ }
252
+ }