@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,14 @@
1
+ <script setup lang="ts">
2
+ import type { MenubarLabelProps } from 'reka-ui'
3
+ import type { HTMLAttributes } from 'vue'
4
+ import { MenubarLabel } from 'reka-ui'
5
+ import { cn } from '@/lib/utils'
6
+
7
+ const props = defineProps<MenubarLabelProps & { class?: HTMLAttributes['class'], inset?: boolean }>()
8
+ </script>
9
+
10
+ <template>
11
+ <MenubarLabel :class="cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', props.class)">
12
+ <slot />
13
+ </MenubarLabel>
14
+ </template>
@@ -0,0 +1,12 @@
1
+ <script setup lang="ts">
2
+ import type { MenubarMenuProps } from 'reka-ui'
3
+ import { MenubarMenu } from 'reka-ui'
4
+
5
+ const props = defineProps<MenubarMenuProps>()
6
+ </script>
7
+
8
+ <template>
9
+ <MenubarMenu v-bind="props">
10
+ <slot />
11
+ </MenubarMenu>
12
+ </template>
@@ -0,0 +1,30 @@
1
+ <script setup lang="ts">
2
+ import type { MenubarRootEmits, MenubarRootProps } from 'reka-ui'
3
+ import type { HTMLAttributes } from 'vue'
4
+ import { reactiveOmit } from '@vueuse/core'
5
+ import {
6
+ MenubarRoot,
7
+ useForwardPropsEmits,
8
+ } from 'reka-ui'
9
+ import { cn } from '@/lib/utils'
10
+
11
+ const props = defineProps<MenubarRootProps & { class?: HTMLAttributes['class'] }>()
12
+ const emits = defineEmits<MenubarRootEmits>()
13
+
14
+ const delegatedProps = reactiveOmit(props, 'class')
15
+
16
+ const forwarded = useForwardPropsEmits(delegatedProps, emits)
17
+ </script>
18
+
19
+ <template>
20
+ <MenubarRoot
21
+ v-bind="forwarded"
22
+ :class="
23
+ cn(
24
+ props.class,
25
+ )
26
+ "
27
+ >
28
+ <slot />
29
+ </MenubarRoot>
30
+ </template>
@@ -0,0 +1,17 @@
1
+ <script setup lang="ts">
2
+ import type { MenubarSeparatorProps } from 'reka-ui'
3
+ import type { HTMLAttributes } from 'vue'
4
+ import { reactiveOmit } from '@vueuse/core'
5
+ import { MenubarSeparator, useForwardProps } from 'reka-ui'
6
+ import { cn } from '@/lib/utils'
7
+
8
+ const props = defineProps<MenubarSeparatorProps & { class?: HTMLAttributes['class'] }>()
9
+
10
+ const delegatedProps = reactiveOmit(props, 'class')
11
+
12
+ const forwardedProps = useForwardProps(delegatedProps)
13
+ </script>
14
+
15
+ <template>
16
+ <MenubarSeparator :class=" cn('-mx-1 my-1 h-[0.5px] bg-gray-200', props.class)" v-bind="forwardedProps" />
17
+ </template>
@@ -0,0 +1,14 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue'
3
+ import { cn } from '@/lib/utils'
4
+
5
+ const props = defineProps<{
6
+ class?: HTMLAttributes['class']
7
+ }>()
8
+ </script>
9
+
10
+ <template>
11
+ <span :class="cn('ml-auto text-xs tracking-widest text-gray-600', props.class)">
12
+ <slot />
13
+ </span>
14
+ </template>
@@ -0,0 +1,36 @@
1
+ <script setup lang="ts">
2
+ import type { MenubarSubContentEmits, MenubarSubContentProps } from 'reka-ui'
3
+ import type { HTMLAttributes } from 'vue'
4
+ import { reactiveOmit } from '@vueuse/core'
5
+ import {
6
+ MenubarPortal,
7
+ MenubarSubContent,
8
+
9
+ useForwardPropsEmits,
10
+ } from 'reka-ui'
11
+ import { cn } from '@/lib/utils'
12
+
13
+ const props = defineProps<MenubarSubContentProps & { class?: HTMLAttributes['class'] }>()
14
+
15
+ const emits = defineEmits<MenubarSubContentEmits>()
16
+
17
+ const delegatedProps = reactiveOmit(props, 'class')
18
+
19
+ const forwarded = useForwardPropsEmits(delegatedProps, emits)
20
+ </script>
21
+
22
+ <template>
23
+ <MenubarPortal>
24
+ <MenubarSubContent
25
+ v-bind="forwarded"
26
+ :class="
27
+ cn(
28
+ 'z-50 min-w-32 overflow-hidden ml-1 rounded-md border-[1px] bg-popover p-1 text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
29
+ props.class,
30
+ )
31
+ "
32
+ >
33
+ <slot />
34
+ </MenubarSubContent>
35
+ </MenubarPortal>
36
+ </template>
@@ -0,0 +1,28 @@
1
+ <script setup lang="ts">
2
+ import type { MenubarSubTriggerProps } from 'reka-ui'
3
+ import type { HTMLAttributes } from 'vue'
4
+ import { reactiveOmit } from '@vueuse/core'
5
+ import { ChevronRight } from 'lucide-vue-next'
6
+ import { MenubarSubTrigger, useForwardProps } from 'reka-ui'
7
+ import { cn } from '@/lib/utils'
8
+
9
+ const props = defineProps<MenubarSubTriggerProps & { class?: HTMLAttributes['class'], inset?: boolean }>()
10
+
11
+ const delegatedProps = reactiveOmit(props, 'class')
12
+
13
+ const forwardedProps = useForwardProps(delegatedProps)
14
+ </script>
15
+
16
+ <template>
17
+ <MenubarSubTrigger
18
+ v-bind="forwardedProps"
19
+ :class="cn(
20
+ 'flex cursor-pointer select-none items-center rounded-md px-2 py-1.5 text-sm outline-none focus:bg-gray-200 data-[state=open]:bg-gray-200',
21
+ inset && 'pl-8',
22
+ props.class,
23
+ )"
24
+ >
25
+ <slot />
26
+ <ChevronRight class="ml-auto h-4 w-4" />
27
+ </MenubarSubTrigger>
28
+ </template>
@@ -0,0 +1,20 @@
1
+ <script setup lang="ts">
2
+ import type { MenubarSubEmits } from 'reka-ui'
3
+ import { MenubarSub, useForwardPropsEmits } from 'reka-ui'
4
+
5
+ interface MenubarSubRootProps {
6
+ defaultOpen?: boolean
7
+ open?: boolean
8
+ }
9
+
10
+ const props = defineProps<MenubarSubRootProps>()
11
+ const emits = defineEmits<MenubarSubEmits>()
12
+
13
+ const forwarded = useForwardPropsEmits(props, emits)
14
+ </script>
15
+
16
+ <template>
17
+ <MenubarSub v-bind="forwarded">
18
+ <slot />
19
+ </MenubarSub>
20
+ </template>
@@ -0,0 +1,27 @@
1
+ <script setup lang="ts">
2
+ import type { MenubarTriggerProps } from 'reka-ui'
3
+ import type { HTMLAttributes } from 'vue'
4
+ import { reactiveOmit } from '@vueuse/core'
5
+ import { MenubarTrigger, useForwardProps } from 'reka-ui'
6
+ import { cn } from '@/lib/utils'
7
+
8
+ const props = defineProps<MenubarTriggerProps & { class?: HTMLAttributes['class'] }>()
9
+
10
+ const delegatedProps = reactiveOmit(props, 'class')
11
+
12
+ const forwardedProps = useForwardProps(delegatedProps)
13
+ </script>
14
+
15
+ <template>
16
+ <MenubarTrigger
17
+ v-bind="forwardedProps"
18
+ :class="
19
+ cn(
20
+ 'flex items-center gap-2 cursor-pointer select-none px-2 py-1.5 text-sm font-medium outline-none rounded-lg hover:bg-gray-200 data-[state=open]:bg-gray-200',
21
+ props.class,
22
+ )
23
+ "
24
+ >
25
+ <slot />
26
+ </MenubarTrigger>
27
+ </template>
@@ -0,0 +1,298 @@
1
+ <script setup lang="ts">
2
+ import { cn } from '@/lib/utils'
3
+
4
+ const props = defineProps<{
5
+ modelValue?: string
6
+ readOnly?: boolean
7
+ size?: 'xs' | 'sm' | 'md'
8
+ allowSearch?: boolean
9
+ options: SelectMenuOptions
10
+ compact?: boolean
11
+ noBorder?: string
12
+ placeholder?: string
13
+ required?: boolean
14
+ iconWithBackground?: {
15
+ size: 'xs' | 'sm' | 'md'
16
+ backgroundClass: string
17
+ }
18
+ triggerClass?: string
19
+ }>()
20
+
21
+ const emit = defineEmits<{
22
+ 'update:modelValue': [value: string]
23
+ 'navigate': [to: string]
24
+ 'select': [value: string]
25
+ 'focus': []
26
+ 'blur': []
27
+ }>()
28
+
29
+ const innerValue = ref<string>('')
30
+ const container = ref<HTMLElement>()
31
+ const triggerElementRef = ref<HTMLElement | null>(null)
32
+
33
+ const isFocused = ref(false)
34
+ const isOpen = ref(false)
35
+ watch(isFocused, val => val ? emit('focus') : emit('blur'))
36
+
37
+ function handleEscKey(e: KeyboardEvent) {
38
+ if (e.key === 'Escape') {
39
+ isFocused.value = false
40
+ isOpen.value = false
41
+
42
+ if (triggerElementRef.value) {
43
+ const button = triggerElementRef.value.querySelector('button')
44
+ if (button) {
45
+ button.blur()
46
+ }
47
+ }
48
+
49
+ e.stopPropagation()
50
+ }
51
+ }
52
+
53
+ onMounted(() => {
54
+ const button = triggerElementRef.value?.querySelector('button')
55
+ if (button) {
56
+ const handleFocus = () => {
57
+ isFocused.value = true
58
+ }
59
+ const handleBlur = () => {
60
+ isFocused.value = false
61
+ }
62
+ button.addEventListener('focus', handleFocus)
63
+ button.addEventListener('blur', handleBlur)
64
+
65
+ onBeforeUnmount(() => {
66
+ button.removeEventListener('focus', handleFocus)
67
+ button.removeEventListener('blur', handleBlur)
68
+ })
69
+ }
70
+ })
71
+
72
+ const testId = ref('')
73
+ onMounted(() => {
74
+ if (container.value?.attributes && container.value?.attributes.getNamedItem('data-testid')) {
75
+ testId.value = container.value?.attributes.getNamedItem('data-testid')?.value as string
76
+
77
+ // Remove the data-testid attribute from the container element
78
+ container.value?.removeAttribute('data-testid')
79
+
80
+ // Add the data-testid attribute to the trigger element
81
+ triggerElementRef.value?.setAttribute('data-testid', testId.value)
82
+ }
83
+
84
+ window.addEventListener('keydown', handleEscKey)
85
+
86
+ const button = triggerElementRef.value?.querySelector('button')
87
+ if (button) {
88
+ const handleFocus = () => {
89
+ isFocused.value = true
90
+ }
91
+ const handleBlur = () => {
92
+ isFocused.value = false
93
+ }
94
+ button.addEventListener('focus', handleFocus)
95
+ button.addEventListener('blur', handleBlur)
96
+
97
+ button.addEventListener('keydown', handleEscKey)
98
+
99
+ onBeforeUnmount(() => {
100
+ window.removeEventListener('keydown', handleEscKey)
101
+ button.removeEventListener('focus', handleFocus)
102
+ button.removeEventListener('blur', handleBlur)
103
+ button.removeEventListener('keydown', handleEscKey)
104
+ })
105
+ }
106
+ })
107
+
108
+ const hasGroups = computed(() => props.options.some(option => option.group))
109
+ const groupedOptions = computed(() => {
110
+ if (!hasGroups.value)
111
+ return { __default: props.options }
112
+
113
+ const groups: Record<string, typeof props.options> = {}
114
+ props.options.forEach((option) => {
115
+ const groupKey = option.group ? option.group : '__default'
116
+
117
+ if (!groups[groupKey])
118
+ groups[groupKey] = []
119
+
120
+ groups[groupKey].push(option)
121
+ })
122
+ return groups
123
+ })
124
+
125
+ const currentOption = computed(() => {
126
+ if (!innerValue.value && !props.modelValue && props.placeholder) {
127
+ return { label: props.placeholder, value: '' }
128
+ }
129
+ return props.options.find(option => option.value === innerValue.value || option.value === props.modelValue) ?? props.options[0]
130
+ })
131
+
132
+ const triggerClasses = computed(() => {
133
+ const isPlaceholderShown = props.placeholder && !innerValue.value && !props.modelValue
134
+
135
+ return cn(
136
+ // Base layout & appearance
137
+ 'bg-white text-textcolor b-1 b-gray-200 p-8px w-full outline-none transition',
138
+ 'focus:b-#08344D/70 focus:ring-2 focus:ring-#08344D/12 data-[placeholder]:text-textcolor/50',
139
+
140
+ // Size variants
141
+ props.compact ? 'rounded-28px px-16px py-6px' : 'rounded-8px',
142
+
143
+ // State modifiers
144
+ props.noBorder && 'b-0 pl-0 !py-0',
145
+ isPlaceholderShown && '!text-gray-600 !font-light',
146
+ props.triggerClass,
147
+ )
148
+ })
149
+
150
+ function handleItemSelect(option: any) {
151
+ if (props.readOnly)
152
+ return
153
+
154
+ innerValue.value = option.value
155
+ emit('select', option.value)
156
+ emit('update:modelValue', option.value)
157
+
158
+ if (option.to) {
159
+ navigateTo(option.to)
160
+ }
161
+ }
162
+ </script>
163
+
164
+ <template>
165
+ <div ref="container">
166
+ <TelaMenubarRoot>
167
+ <TelaMenubarMenu>
168
+ <div
169
+ ref="triggerElementRef"
170
+ v-bind="$attrs"
171
+ @keydown.esc.stop="handleEscKey($event)"
172
+ >
173
+ <slot
174
+ v-bind="{ currentOption }"
175
+ name="trigger"
176
+ >
177
+ <TelaMenubarTrigger
178
+ :class="triggerClasses"
179
+ :disabled="readOnly"
180
+ aria-label="Customise options"
181
+ >
182
+ <div flex items-center w-full gap-8px>
183
+ <div
184
+ v-if="currentOption?.icon && !(currentOption.value === '')"
185
+ mr-12px rounded-4px w-40px h-40px flex items-center justify-center b="0.5px #E1E3E4"
186
+ text-24px
187
+ :class="[
188
+ compact && '!text-16px b-0 w-auto h-auto !mr-0px leading-0',
189
+ ]"
190
+ >
191
+ <TelaIcon v-if="typeof currentOption.icon === 'string'" :name="currentOption.icon" />
192
+ <Component :is="currentOption.icon" v-else />
193
+ </div>
194
+ <div v-if="!currentOption?.icon && currentOption?.externalIconSrc" mr-4px rounded-4px w-16px h-20px flex items-center justify-center>
195
+ <img :src="currentOption.externalIconSrc" alt="" w-16px h-16px>
196
+ </div>
197
+ <span
198
+ :class="[
199
+ compact ? 'body-12-semibold' : 'body-14-medium',
200
+ (placeholder && !innerValue && !props.modelValue) ? 'text-gray-400 font-light' : '',
201
+ ]"
202
+ text-left flex-auto
203
+ >
204
+ {{ currentOption?.label }}
205
+ </span>
206
+ <div
207
+ i-ph-caret-up-down op-60
208
+ :class="[
209
+ compact ? '!text-10px' : '!text-16px',
210
+ ]"
211
+ />
212
+ </div>
213
+ </TelaMenubarTrigger>
214
+ </slot>
215
+ </div>
216
+
217
+ <TelaMenubarContent
218
+ :side-offset="4"
219
+ :class="cn(
220
+ 'bg-white',
221
+ triggerClass || 'rounded-8px',
222
+ 'flying-shadow',
223
+ 'b-0.5px b-gray-300',
224
+ 'overflow-hidden',
225
+ 'will-change-[opacity,transform] data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade',
226
+ )"
227
+ :style="{
228
+ width: compact ? 'fit-content' : 'auto',
229
+ minWidth: compact ? undefined : '328px',
230
+ }"
231
+ avoid-collisions
232
+ max-h-400px
233
+ z-999
234
+ >
235
+ <div class="p-5px w-full max-h-400px overflow-y-auto">
236
+ <template
237
+ v-for="[group, groupOptions], idx of Object.entries(groupedOptions!)"
238
+ :key="idx"
239
+ >
240
+ <div v-if="hasGroups && group !== '__default'" body-12-regular text="gray-700" mt-4px p="6px" pb-8px capitalize>
241
+ {{ group }}
242
+ </div>
243
+ <TelaMenubarItem
244
+ v-for="option in groupOptions"
245
+ :key="option.value"
246
+ :data-testid="testId ? `${testId}:option:${option.value}` : undefined"
247
+ class="p-6px rounded-4px transition outline-none select-none flex w-full items-center hover:cursor-pointer text-gray-900 hover:bg-#F5F6F6 focus:bg-#F5F6F6"
248
+ :class="[
249
+ option.value === (innerValue || modelValue) && 'bg-#F5F6F6',
250
+ ]"
251
+ @click="handleItemSelect(option)"
252
+ >
253
+ <TelaTooltip
254
+ side="right"
255
+ :disabled="!option.tooltipContent"
256
+ :delay-duration="200"
257
+ >
258
+ <template #content>
259
+ <component :is="option.tooltipContent" />
260
+ </template>
261
+ <div
262
+ v-if="option.icon" mr-12px rounded-4px w-40px h-40px flex items-center justify-center
263
+ b="0.5px gray-200"
264
+ text="gray-900"
265
+ bg-white flex-none
266
+ >
267
+ <TelaIcon v-if="iconWithBackground" :name="option.icon" :size="iconWithBackground?.size" :background-class="iconWithBackground?.backgroundClass" />
268
+ <TelaIcon v-else-if="typeof option.icon === 'string'" size="sm" :name="option.icon" />
269
+ <Component :is="option.icon" v-else class="!w-24px !h-24px" />
270
+ </div>
271
+ <div v-if="!option.icon && option?.externalIconSrc" mr-12px rounded-4px w-16px h-36px flex items-center justify-center>
272
+ <img :src="option.externalIconSrc" alt="" w-18px h-18px>
273
+ </div>
274
+ <div flex="~ col">
275
+ <p text="gray-900" body-14-medium text-start>
276
+ {{ option.label }}
277
+ </p>
278
+
279
+ <span v-if="option.description" text="gray-700" body-12-regular text-start>
280
+ {{ option.description }}
281
+ </span>
282
+ </div>
283
+ <div flex-auto />
284
+ <div v-if="option.value === (innerValue || modelValue)" flex items-center justify-center>
285
+ <div i-ph-check />
286
+ </div>
287
+ </TelaTooltip>
288
+ </TelaMenubarItem>
289
+ </template>
290
+ </div>
291
+ </TelaMenubarContent>
292
+ </TelaMenubarMenu>
293
+ </TelaMenubarRoot>
294
+ </div>
295
+ </template>
296
+
297
+ <style>
298
+ </style>
@@ -0,0 +1,145 @@
1
+ import { Meta, ArgTypes } from '@storybook/blocks';
2
+
3
+ <Meta title="Layout/Modal" />
4
+
5
+ # TelaModal
6
+
7
+ A modal dialog component that displays content in a centered overlay. Useful for important messages, confirmations, forms, and content that requires user attention.
8
+
9
+ ## Props
10
+
11
+ <ArgTypes />
12
+
13
+ ```typescript
14
+ type ModalProps = {
15
+ modelValue: boolean
16
+ title?: string
17
+ size?: 'sm' | 'md' | 'lg' | 'xl'
18
+ closeOnOverlay?: boolean
19
+ showClose?: boolean
20
+ }
21
+ ```
22
+
23
+ ## Examples
24
+
25
+ ### Basic Usage
26
+
27
+ ```vue
28
+ <script setup>
29
+ import { ref } from 'vue'
30
+
31
+ const isOpen = ref(false)
32
+ </script>
33
+
34
+ <template>
35
+ <TelaButton @click="isOpen = true">
36
+ Open Modal
37
+ </TelaButton>
38
+
39
+ <TelaModal v-model="isOpen" title="Modal Title">
40
+ <p>Modal content goes here</p>
41
+ </TelaModal>
42
+ </template>
43
+ ```
44
+
45
+ ### With Actions
46
+
47
+ ```vue
48
+ <TelaModal v-model="isOpen" title="Confirm Action">
49
+ <p>Are you sure you want to proceed?</p>
50
+
51
+ <template #footer>
52
+ <div class="flex gap-2 justify-end">
53
+ <TelaButton variant="secondary" @click="isOpen = false">
54
+ Cancel
55
+ </TelaButton>
56
+ <TelaButton variant="primary" @click="confirm">
57
+ Confirm
58
+ </TelaButton>
59
+ </div>
60
+ </template>
61
+ </TelaModal>
62
+ ```
63
+
64
+ ### Different Sizes
65
+
66
+ ```vue
67
+ <!-- Small -->
68
+ <TelaModal v-model="isOpen" size="sm" title="Small Modal">
69
+ <p>Compact modal</p>
70
+ </TelaModal>
71
+
72
+ <!-- Medium (default) -->
73
+ <TelaModal v-model="isOpen" size="md" title="Medium Modal">
74
+ <p>Standard modal</p>
75
+ </TelaModal>
76
+
77
+ <!-- Large -->
78
+ <TelaModal v-model="isOpen" size="lg" title="Large Modal">
79
+ <p>Spacious modal</p>
80
+ </TelaModal>
81
+
82
+ <!-- Extra Large -->
83
+ <TelaModal v-model="isOpen" size="xl" title="Extra Large Modal">
84
+ <p>Very large modal</p>
85
+ </TelaModal>
86
+ ```
87
+
88
+ ### Without Close Button
89
+
90
+ ```vue
91
+ <TelaModal
92
+ v-model="isOpen"
93
+ title="No Close Button"
94
+ :show-close="false"
95
+ >
96
+ <p>Modal without close button</p>
97
+ </TelaModal>
98
+ ```
99
+
100
+ ### Prevent Close on Overlay
101
+
102
+ ```vue
103
+ <TelaModal
104
+ v-model="isOpen"
105
+ title="Must Use Button"
106
+ :close-on-overlay="false"
107
+ >
108
+ <p>Can only be closed with a button</p>
109
+ <template #footer>
110
+ <TelaButton @click="isOpen = false">Close</TelaButton>
111
+ </template>
112
+ </TelaModal>
113
+ ```
114
+
115
+ ## Slots
116
+
117
+ - `default` - Main content area
118
+ - `header` - Custom header content (replaces title)
119
+ - `footer` - Footer area for actions/buttons
120
+
121
+ ## Events
122
+
123
+ - `update:modelValue` - Emitted when modal opens/closes with boolean value
124
+ - `close` - Emitted when modal is closed
125
+
126
+ ## Features
127
+
128
+ - **Centered Layout**: Modal centered in viewport
129
+ - **Overlay**: Semi-transparent backdrop
130
+ - **Multiple Sizes**: Small, medium, large, and extra-large
131
+ - **Close Options**: Close button, overlay click, Escape key
132
+ - **Custom Header/Footer**: Slots for flexible layouts
133
+ - **Scrollable Content**: Long content scrolls within modal
134
+ - **Focus Trap**: Keeps focus within modal
135
+ - **Accessible**: Proper ARIA attributes and keyboard navigation
136
+
137
+ ## Accessibility
138
+
139
+ - Focus trapped within modal when open
140
+ - Escape key to close (unless disabled)
141
+ - Proper ARIA attributes (role="dialog", aria-modal)
142
+ - Focus returns to trigger element on close
143
+ - Close button with accessible label
144
+ - Screen reader announcements
145
+