@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,138 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import ChatMessage from './index.vue'
3
+
4
+ const meta = {
5
+ title: 'Components/Chat/Message',
6
+ component: ChatMessage,
7
+ tags: ['autodocs'],
8
+ parameters: {
9
+ layout: 'centered',
10
+ docs: {
11
+ description: {
12
+ component: 'A chat message component that displays individual messages in a chat interface. Supports avatars, timestamps, replies, loading states, delete actions, and message threading. Used within the Chat component to render message content.',
13
+ },
14
+ },
15
+ },
16
+ argTypes: {
17
+ id: {
18
+ control: 'text',
19
+ description: 'Unique identifier for the message.',
20
+ },
21
+ content: {
22
+ control: 'text',
23
+ description: 'The message text content.',
24
+ },
25
+ timestamp: {
26
+ control: 'object',
27
+ description: 'Date object representing when the message was sent.',
28
+ },
29
+ author: {
30
+ control: 'text',
31
+ description: 'Name of the message author.',
32
+ },
33
+ authorEmail: {
34
+ control: 'text',
35
+ description: 'Email address of the message author.',
36
+ },
37
+ avatarUrl: {
38
+ control: 'text',
39
+ description: 'URL of the author\'s avatar image.',
40
+ },
41
+ version: {
42
+ control: 'text',
43
+ description: 'Version identifier for the message.',
44
+ },
45
+ isReply: {
46
+ control: 'boolean',
47
+ description: 'Whether this message is displayed as a reply to another message.',
48
+ },
49
+ replies: {
50
+ control: 'object',
51
+ description: 'Array of reply message objects nested under this message.',
52
+ },
53
+ loading: {
54
+ control: 'boolean',
55
+ description: 'Show loading state for the message (e.g., while sending).',
56
+ },
57
+ },
58
+ } satisfies Meta<typeof ChatMessage>
59
+
60
+ export default meta
61
+ type Story = StoryObj<typeof meta>
62
+
63
+ export const Default: Story = {
64
+ args: {
65
+ id: '1',
66
+ content: 'This is a sample message content',
67
+ timestamp: new Date('2024-09-18'),
68
+ author: 'John Doe',
69
+ authorEmail: 'john@doe.com',
70
+ version: 'VER-1',
71
+ },
72
+ }
73
+
74
+ export const Loading: Story = {
75
+ args: {
76
+ ...Default.args,
77
+ loading: true,
78
+ },
79
+ }
80
+
81
+ export const WithAvatar: Story = {
82
+ args: {
83
+ ...Default.args,
84
+ avatarUrl: 'https://github.com/shadcn.png',
85
+ },
86
+ }
87
+
88
+ export const WithReplyButton: Story = {
89
+ args: {
90
+ ...Default.args,
91
+ },
92
+ }
93
+
94
+ export const WithReplies: Story = {
95
+ args: {
96
+ ...Default.args,
97
+ replies: [
98
+ {
99
+ id: '1',
100
+ content: 'This is a reply',
101
+ author: 'Jane Doe',
102
+ authorEmail: 'jane@doe.com',
103
+ },
104
+ ],
105
+ },
106
+ }
107
+
108
+ export const AsReply: Story = {
109
+ args: {
110
+ ...Default.args,
111
+ isReply: true,
112
+ },
113
+ }
114
+
115
+ export const WithDeleteAction: Story = {
116
+ args: {
117
+ ...Default.args,
118
+ },
119
+ }
120
+
121
+ export const CompleteExample: Story = {
122
+ args: {
123
+ id: '1',
124
+ content: 'Yep, this is still happening. @thierry can we review the parser?',
125
+ timestamp: new Date('2024-09-18'),
126
+ author: 'Guilherme Oliveira',
127
+ avatarUrl: 'https://github.com/shadcn.png',
128
+ authorEmail: 'guilherme@tela.com',
129
+ replies: [
130
+ {
131
+ id: '1',
132
+ content: 'This is a reply',
133
+ authorEmail: 'jane@doe.com',
134
+ author: 'Jane Doe',
135
+ },
136
+ ],
137
+ },
138
+ }
@@ -0,0 +1,355 @@
1
+ <script setup lang="ts">
2
+ import { TreeItem } from 'radix-vue'
3
+ import { isToday } from 'date-fns'
4
+ import { computed } from 'vue'
5
+ import type { User } from '../types'
6
+
7
+ interface ChatMessageProps {
8
+ id: string
9
+ content: string
10
+ timestamp?: Date | string
11
+ author: string
12
+ authorEmail: string
13
+ version?: string
14
+ avatarUrl?: string
15
+ isReply?: boolean
16
+ loading?: boolean
17
+ depth?: number
18
+ replies?: ChatMessageProps[]
19
+ allowReply?: boolean
20
+ parentElement?: HTMLElement
21
+ isChat?: boolean
22
+ currentUser?: User
23
+ otherUsers?: User[]
24
+ isExpanded?: boolean
25
+ }
26
+
27
+ const props = defineProps<ChatMessageProps>()
28
+ const emit = defineEmits<{
29
+ reply: []
30
+ delete: [id: string]
31
+ toggleExpand: []
32
+ }>()
33
+
34
+ const treeItemRef = ref<HTMLElement>()
35
+ const heightValue = ref(0)
36
+ const isExpandedManually = ref(props.isExpanded ?? false)
37
+ const isMounted = ref(false)
38
+ const isUnmounting = ref(false)
39
+
40
+ // Wrap TreeItem content in a component
41
+ const MessageContent = defineComponent({
42
+ inheritAttrs: false,
43
+ props: {
44
+ isExpanded: Boolean,
45
+ handleToggle: Function,
46
+ },
47
+ setup(props, { slots }) {
48
+ return () => slots.default?.({
49
+ isExpanded: props.isExpanded,
50
+ handleToggle: () => props.handleToggle?.(),
51
+ })
52
+ },
53
+ })
54
+
55
+ onMounted(() => {
56
+ isMounted.value = true
57
+ })
58
+
59
+ onBeforeUnmount(() => {
60
+ isUnmounting.value = true
61
+ })
62
+
63
+ async function calculateTotalHeight(message: ChatMessageProps): Promise<number> {
64
+ try {
65
+ await nextTick()
66
+
67
+ // Guard against unmounting during the async operation
68
+ if (isUnmounting.value) {
69
+ return 0
70
+ }
71
+
72
+ const contentElement = document.getElementById(`message-content-${message.id}`)
73
+ const baseHeight = contentElement?.clientHeight ?? 1
74
+
75
+ if (message.replies?.length) {
76
+ const repliesHeight = await Promise.all(
77
+ message.replies.map(reply => calculateTotalHeight(reply)),
78
+ ).then(heights =>
79
+ heights.reduce((acc, height) => acc + height, 0),
80
+ )
81
+ return baseHeight + repliesHeight
82
+ }
83
+
84
+ return baseHeight
85
+ }
86
+ catch (error) {
87
+ console.error('Error calculating total height', error)
88
+ return 0
89
+ }
90
+ }
91
+
92
+ const hasReplies = computed(() => props.replies && props.replies.length > 0)
93
+ const { t, locale } = useI18n()
94
+
95
+ const timestampString = computed(() => {
96
+ if (!props.timestamp)
97
+ return ''
98
+
99
+ const date = new Date(props.timestamp)
100
+ if (isToday(date))
101
+ return t('chat.todayAt', { time: date.toLocaleTimeString(locale.value ?? 'en-US', { hour: '2-digit', minute: '2-digit', hour12: false }) })
102
+
103
+ return t('chat.onDate', { date: date.toLocaleDateString(locale.value ?? 'en-US', { day: '2-digit', month: 'short', year: 'numeric' }) })
104
+ })
105
+
106
+ const beforeWidth = computed(() => {
107
+ if (props.parentElement) {
108
+ if (hasReplies.value) {
109
+ return `${(props.depth ?? 1) * 24}px`
110
+ }
111
+ if (props.depth && props.depth <= 2) {
112
+ return `80px`
113
+ }
114
+ }
115
+ return '0px'
116
+ })
117
+
118
+ const resolvedContent = computed(() => {
119
+ if (props.currentUser && props.otherUsers) {
120
+ return props.content.replace(/@([\w.-]+@[\w.-]+\.\w+)/g, (match, email) => {
121
+ const user = props.otherUsers?.find(user => user.email === email)
122
+ return user ? `<span class="mention" data-type="mention" data-label="${user.name}">@${user.name}</span>` : match
123
+ })
124
+ }
125
+ return props.content
126
+ })
127
+
128
+ const totalHeight = computed(() => heightValue.value ? `${heightValue.value}px` : '0px')
129
+
130
+ watch([() => props.replies, isMounted], async () => {
131
+ if (!isMounted.value || isUnmounting.value)
132
+ return
133
+
134
+ const height = await calculateTotalHeight(props)
135
+ if (!isUnmounting.value) {
136
+ heightValue.value = height + 10
137
+ }
138
+ }, { immediate: true })
139
+
140
+ watch(isExpandedManually, async () => {
141
+ const height = await calculateTotalHeight(props)
142
+ heightValue.value = height
143
+ }, { immediate: true })
144
+ </script>
145
+
146
+ <template>
147
+ <component
148
+ :is="isChat ? TreeItem : MessageContent" ref="treeItemRef"
149
+ :key="props.id"
150
+ v-slot="slotProps"
151
+ :level="props.depth ?? 1"
152
+ v-bind="{ value: { id: props.id, children: props.replies } }"
153
+ class="mb-16px"
154
+ :cursor="hasReplies ? 'pointer' : 'default'"
155
+ @click.stop="(e: Event) => {
156
+ isExpandedManually = !isExpandedManually
157
+ }"
158
+ >
159
+ <div
160
+ class="message-container"
161
+ :class="[
162
+ hasReplies ? `
163
+ after:absolute
164
+ after:left-6
165
+ after:top-8
166
+ after:bottom-0
167
+ after:w-[1px]
168
+ after:bg-gray-300
169
+ after:z-1
170
+ after:h-${totalHeight}`
171
+ : '',
172
+ isReply ? `
173
+ before:absolute
174
+ before:left-[-40px]
175
+ before:top-3.5
176
+ before:h-[12px]
177
+ before:w-[20px]
178
+ before:border-l
179
+ before:border-b
180
+ before:border-gray-300
181
+ before:rounded-bl-[12px]
182
+ before:z-1
183
+ ml-10
184
+ mt-2
185
+ ` : '',
186
+ { 'before:w-[80px]': props.isReply },
187
+ ]"
188
+ :style="{
189
+ '--before-width': beforeWidth,
190
+ '--after-height': totalHeight,
191
+ }"
192
+ >
193
+ <div :id="`message-content-${props.id}`" class="flex-1 p-8px">
194
+ <div class="flex items-start gap-3">
195
+ <TelaTooltip v-if="!props.loading" :content="author">
196
+ <TelaAvatar
197
+ :image="avatarUrl"
198
+ :alt="author"
199
+ size="sm"
200
+ rounded="full"
201
+ class="h-8 w-8 shrink-0 relative z-10 bg-white"
202
+ />
203
+ </TelaTooltip>
204
+ <div v-else>
205
+ <TelaSkeleton class="w-40px h-40px rounded-full" />
206
+ </div>
207
+
208
+ <div class="flex-1">
209
+ <div class="flex items-center gap-2">
210
+ <div class="flex flex-col mb-2">
211
+ <span v-if="!props.loading" class="body-14-semibold text-gray-800">{{ author }}</span>
212
+ <TelaSkeleton v-else class="w-100px h-14px mb-2" />
213
+ <div v-if="!props.loading" flex="~ gap-1 items-center">
214
+ <span class="body-12-regular text-gray-600">{{ timestampString }}</span>
215
+ <TelaIcon v-if="version" name="i-ph-dot" color="gray-300" />
216
+ <div v-if="version" py-2px px-4px rounded-5px bg-gray-200 h-16px>
217
+ <p body-caption-9-bold text-gray-600>
218
+ {{ version }}
219
+ </p>
220
+ </div>
221
+ </div>
222
+ <TelaSkeleton v-else class="w-80px h-8px" />
223
+ </div>
224
+ <div flex-auto />
225
+ <TelaDropdownMenu
226
+ v-if="!props.loading"
227
+ :disabled="props.id === 'new'"
228
+ :items="[
229
+ {
230
+ label: 'Delete',
231
+ color: 'negative',
232
+ disabled: !props.authorEmail
233
+ || !props.currentUser?.email
234
+ || props.authorEmail !== props.currentUser?.email,
235
+ icon: 'i-ph-trash-simple-bold',
236
+ click: () => emit('delete', props.id),
237
+ },
238
+ ]"
239
+ >
240
+ <TelaIconButton
241
+ size="3xs"
242
+ color="secondary"
243
+ icon="i-ph-dots-three-vertical-bold"
244
+ />
245
+ </TelaDropdownMenu>
246
+ </div>
247
+
248
+ <div v-if="!props.loading" class="mt-1 body-16-regular text-gray-800">
249
+ <span v-html="resolvedContent" />
250
+ </div>
251
+ <div v-else flex="~ col gap-8px">
252
+ <TelaSkeleton
253
+ v-for="width in [50, 30]"
254
+ :key="width"
255
+ :style="{ width: `${width}%` }"
256
+ h-12px
257
+ />
258
+ </div>
259
+
260
+ <div v-if="!props.loading" class="flex items-start gap-2 mt-8px">
261
+ <TelaButton
262
+ v-if="allowReply"
263
+ variant="ghost"
264
+ size="sm"
265
+ b="0.5px solid gray-300"
266
+ bg-white
267
+ :disabled="props.id === 'new'"
268
+ class="text-gray-900 body-12-semibold"
269
+ @click.stop="emit('reply')"
270
+ >
271
+ <TelaIcon name="i-ph-arrow-bend-down-right" size="sm" />
272
+ Reply
273
+ </TelaButton>
274
+ </div>
275
+ </div>
276
+ </div>
277
+ <TelaIconButton
278
+ v-if="hasReplies"
279
+ z-50
280
+ size="2xs"
281
+ color="secondary"
282
+ icon="i-ph-caret-down"
283
+ b="0.5px solid gray-300"
284
+ h-24px
285
+ w-24px
286
+ :class="{ 'rotate-0': slotProps.isExpanded, '-rotate-90': !slotProps.isExpanded }"
287
+ class="absolute top-70px left-12px z-10 bg-white"
288
+ @click.stop="(e: Event) => {
289
+ slotProps.handleToggle(e)
290
+ isExpandedManually = !isExpandedManually
291
+ }"
292
+ />
293
+ </div>
294
+ </div>
295
+
296
+ <!-- Recursively render replies as nested TreeItems -->
297
+ <template v-if="hasReplies && slotProps.isExpanded">
298
+ <component
299
+ :is="isChat ? TreeItem : 'div'"
300
+ v-for="reply in replies"
301
+ :key="reply.id"
302
+ :value="{ id: reply.id }"
303
+ :level="(props.depth ?? 1) + 1"
304
+ class="mt-10px ml-24px"
305
+ :style="{
306
+ width: `calc(100% - 62px)`,
307
+ }"
308
+ >
309
+ <TelaChatTextMessage
310
+ :id="reply.id"
311
+ :content="reply.content"
312
+ :timestamp="reply.timestamp"
313
+ :author="reply.author"
314
+ :author-email="reply.authorEmail"
315
+ :avatar-url="reply.avatarUrl"
316
+ :is-reply="true"
317
+ :depth="(props.depth ?? 1) + 1"
318
+ :replies="reply.replies"
319
+ :parent-element="treeItemRef ?? undefined"
320
+ :allow-reply="false"
321
+ :current-user="props.currentUser"
322
+ :other-users="props.otherUsers"
323
+ :is-expanded="isExpandedManually"
324
+ @delete="() => emit('delete', reply.id)"
325
+ />
326
+ </component>
327
+ </template>
328
+ </component>
329
+ </template>
330
+
331
+ <style scoped>
332
+ .message-container {
333
+ display: flex;
334
+ flex-direction: row;
335
+ width: 100%;
336
+ gap: 3px;
337
+ position: relative;
338
+ }
339
+
340
+ .message-container::before {
341
+ width: var(--before-width);
342
+ }
343
+
344
+ .message-container::after {
345
+ height: var(--after-height);
346
+ }
347
+
348
+ .rotate-0 {
349
+ transform: rotate(0deg);
350
+ }
351
+
352
+ .-rotate-90 {
353
+ transform: rotate(-90deg);
354
+ }
355
+ </style>
@@ -0,0 +1,19 @@
1
+ export interface Message {
2
+ id: string
3
+ content: string
4
+ timestamp?: Date
5
+ author: string
6
+ authorEmail: string
7
+ avatarUrl?: string
8
+ version?: string
9
+ isReply?: boolean
10
+ replies?: Message[]
11
+ parentId?: string
12
+ }
13
+
14
+ export type User = {
15
+ id: string
16
+ name: string
17
+ email: string
18
+ avatarUrl: string
19
+ }
@@ -0,0 +1,30 @@
1
+ <script lang="ts" setup>
2
+ import { Label, useForwardPropsEmits } from 'reka-ui'
3
+ import type { CheckboxRootProps, CheckboxRootEmits } from 'reka-ui'
4
+ import type { HTMLAttributes } from 'vue'
5
+
6
+ const props = defineProps<CheckboxRootProps & { label: string, description: string, class?: HTMLAttributes['class'] }>()
7
+ const emits = defineEmits<CheckboxRootEmits>()
8
+
9
+ const forwarded = useForwardPropsEmits(props, emits)
10
+ </script>
11
+
12
+ <template>
13
+ <Label
14
+ :class="cn(
15
+ 'w-[380px] p-[12px] flex items-center gap-[12px] rounded-[8px] bg-white-1000 cursor-pointer border-[0.5px] border-gray-200 transition-colors disabled:cursor-not-allowed disabled:bg-gray-100 disabled:border-gray-200',
16
+ 'has-[[aria-checked=false]]:hover:bg-gray-50 has-[[aria-checked=false]]:hover:border-gray-300 has-[[aria-checked=true]]:bg-blue-100/60 has-[[aria-checked=true]]:border-blue-700',
17
+ props.class,
18
+ )"
19
+ >
20
+ <TelaCheckbox v-bind="forwarded" />
21
+ <div>
22
+ <h4 class="body-12-medium text-gray-900">
23
+ {{ label }}
24
+ </h4>
25
+ <p class="body-12-regular text-gray-500">
26
+ {{ description }}
27
+ </p>
28
+ </div>
29
+ </Label>
30
+ </template>
@@ -0,0 +1,164 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as CheckboxStories from './checkbox.stories.ts';
3
+
4
+ <Meta of={CheckboxStories} />
5
+
6
+ # TelaCheckbox
7
+
8
+ A checkbox component built on reka-ui. Supports v-model binding, disabled states, and integrates with forms. Features smooth check animations and includes a checkbox card variant for more complex use cases with labels and descriptions.
9
+
10
+ ## Examples
11
+
12
+ ### With Label
13
+
14
+ <Canvas of={CheckboxStories.WithLabel} />
15
+
16
+ ### Disabled
17
+
18
+ <Canvas of={CheckboxStories.Disabled} />
19
+
20
+ ### With Model Value
21
+
22
+ <Canvas of={CheckboxStories.WithModelValue} />
23
+
24
+ ### Checkbox Card
25
+
26
+ <Canvas of={CheckboxStories.Card} />
27
+
28
+ ### Basic Usage
29
+
30
+ ```vue
31
+ <script setup>
32
+ import { ref } from 'vue'
33
+
34
+ const checked = ref(false)
35
+ </script>
36
+
37
+ <template>
38
+ <TelaCheckbox v-model="checked" />
39
+ </template>
40
+ ```
41
+
42
+ ### With Label Code
43
+
44
+ ```vue
45
+ <script setup>
46
+ import { ref } from 'vue'
47
+
48
+ const accepted = ref(false)
49
+ </script>
50
+
51
+ <template>
52
+ <div class="flex items-center gap-2">
53
+ <TelaCheckbox id="terms" v-model="accepted" />
54
+ <label for="terms" class="cursor-pointer">
55
+ I accept the terms and conditions
56
+ </label>
57
+ </div>
58
+ </template>
59
+ ```
60
+
61
+ ### Disabled State
62
+
63
+ ```vue
64
+ <TelaCheckbox disabled :model-value="true" />
65
+ <TelaCheckbox disabled :model-value="false" />
66
+ ```
67
+
68
+ ### In Forms
69
+
70
+ ```vue
71
+ <script setup>
72
+ import { ref } from 'vue'
73
+
74
+ const preferences = ref([])
75
+ </script>
76
+
77
+ <template>
78
+ <form>
79
+ <div class="flex items-center gap-2">
80
+ <TelaCheckbox
81
+ id="notifications"
82
+ v-model="preferences"
83
+ value="notifications"
84
+ name="preferences"
85
+ />
86
+ <label for="notifications">Enable notifications</label>
87
+ </div>
88
+
89
+ <div class="flex items-center gap-2">
90
+ <TelaCheckbox
91
+ id="newsletter"
92
+ v-model="preferences"
93
+ value="newsletter"
94
+ name="preferences"
95
+ />
96
+ <label for="newsletter">Subscribe to newsletter</label>
97
+ </div>
98
+ </form>
99
+ </template>
100
+ ```
101
+
102
+ ### Checkbox Card Code
103
+
104
+ ```vue
105
+ <TelaCheckboxCard
106
+ label="Make available to all workspaces"
107
+ description="All workspaces will have access to this template."
108
+ />
109
+ ```
110
+
111
+ ## Props
112
+
113
+ <ArgTypes />
114
+
115
+ ```typescript
116
+ type CheckboxProps = {
117
+ modelValue?: boolean
118
+ disabled?: boolean
119
+ required?: boolean
120
+ name?: string
121
+ value?: string
122
+ class?: string
123
+ }
124
+ ```
125
+
126
+ ## Features
127
+
128
+ - **Two-way Binding**: Full v-model support for reactive state
129
+ - **Smooth Animation**: Check mark animates in with path drawing effect
130
+ - **Disabled State**: Proper disabled styling with grayed out appearance
131
+ - **Form Integration**: Works with native form submission
132
+ - **Hover States**: Interactive hover feedback
133
+ - **Focus Ring**: Keyboard navigation with visible focus indicator
134
+ - **Accessible**: Built on reka-ui with proper ARIA attributes
135
+ - **Card Variant**: TelaCheckboxCard for enhanced layouts with labels and descriptions
136
+
137
+ ## Components
138
+
139
+ - `TelaCheckbox` - Standard checkbox component
140
+ - `TelaCheckboxCard` - Enhanced checkbox with label and description layout
141
+
142
+ ## States
143
+
144
+ - **Unchecked**: White background with gray border
145
+ - **Checked**: Dark background with white checkmark
146
+ - **Hover Unchecked**: Light gray background
147
+ - **Hover Checked**: Slightly lighter dark background
148
+ - **Disabled**: Reduced opacity, cursor not-allowed
149
+
150
+ ## Animation
151
+
152
+ The checkbox uses Motion components for smooth check mark animation:
153
+ - Scale animation from 0.85 to 1
154
+ - Path length animation from 0 to 1 (drawing effect)
155
+ - 200ms duration with custom easing
156
+
157
+ ## Accessibility
158
+
159
+ - Built on reka-ui's CheckboxRoot and CheckboxIndicator
160
+ - Proper ARIA attributes for screen readers
161
+ - Keyboard support (Space to toggle)
162
+ - Focus visible ring for keyboard navigation
163
+ - Works with form labels using `id` and `for` attributes
164
+ - Disabled state properly conveyed