@livestorm/ui-vue 2.0.5 → 4.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 (302) hide show
  1. package/README.md +9 -6
  2. package/components-vite.js +2 -2
  3. package/dist/UiButton-uyW8EHRJ.mjs +602 -0
  4. package/dist/UiTimelineItem-EiOEi1yF.mjs +7293 -0
  5. package/dist/UiTooltip-DZcPD6_N.mjs +158 -0
  6. package/dist/a11y-CaZxRWzo.mjs +6 -0
  7. package/dist/breakpoints.mjs +4 -9
  8. package/dist/bus.mjs +5 -5
  9. package/dist/components.mjs +236 -222
  10. package/dist/i18n-B8QOMuFD.mjs +46 -0
  11. package/dist/index.mjs +4 -87
  12. package/dist/integrations.mjs +3 -14
  13. package/dist/layouts.mjs +228 -236
  14. package/dist/responsive.mjs +31 -36
  15. package/dist/slot.mjs +11 -17
  16. package/dist/toastDSV2-D3xDZPe5.mjs +105 -0
  17. package/dist/types/index.d.ts +23 -1
  18. package/dist/types/src/components/Toast/ToastMessage.vue.d.ts +17 -7
  19. package/dist/types/src/components/Toast/toastDSV2.d.ts +89 -64
  20. package/dist/types/src/components/UiActionBar/UiActionBar.vue.d.ts +3 -1
  21. package/dist/types/src/components/UiActionBar/UiActionBarItem.vue.d.ts +18 -3
  22. package/dist/types/src/components/UiActionBar/types.d.ts +5 -1
  23. package/dist/types/src/components/UiAvatar/UiAvatar.vue.d.ts +4 -2
  24. package/dist/types/src/components/UiAvatarList/UiAvatarList.vue.d.ts +3 -2
  25. package/dist/types/src/components/UiBanner/UiBanner.vue.d.ts +13 -1
  26. package/dist/types/src/components/UiButton/UiButton.vue.d.ts +102 -38
  27. package/dist/types/src/components/UiButtonGroup/UiButtonGroup.vue.d.ts +8 -2
  28. package/dist/types/src/components/UiButtonGroup/UiButtonGroupItem.vue.d.ts +3 -0
  29. package/dist/types/src/components/UiButtonGroup/types.d.ts +5 -1
  30. package/dist/types/src/components/UiChoice/UiChoice.vue.d.ts +71 -0
  31. package/dist/types/src/components/UiChoice/index.d.ts +2 -0
  32. package/dist/types/src/components/UiChoice/types.d.ts +7 -0
  33. package/dist/types/src/components/UiColorField/UiColorField.vue.d.ts +1 -1
  34. package/dist/types/src/components/UiColorPicker/UiColorPicker.vue.d.ts +2 -2
  35. package/dist/types/src/components/UiCombobox/UiCombobox.types.d.ts +47 -0
  36. package/dist/types/src/components/UiCombobox/UiCombobox.vue.d.ts +40 -0
  37. package/dist/types/src/components/UiCombobox/index.d.ts +2 -0
  38. package/dist/types/src/components/UiDataTable/UiDataTable.types.d.ts +46 -0
  39. package/dist/types/src/components/UiDataTable/UiDataTable.vue.d.ts +136 -0
  40. package/dist/types/src/components/UiDataTable/index.d.ts +5 -0
  41. package/dist/types/src/components/UiDrawer/UiDrawer.vue.d.ts +28 -0
  42. package/dist/types/src/components/UiDrawer/UiDrawerContent.vue.d.ts +18 -0
  43. package/dist/types/src/components/UiDrawer/UiDrawerTrigger.vue.d.ts +14 -0
  44. package/dist/types/src/components/UiDropdown/DropdownItemCheckbox.vue.d.ts +3 -0
  45. package/dist/types/src/components/UiDropdown/UiDropdownContent.vue.d.ts +10 -0
  46. package/dist/types/src/components/UiDropdown/UiDropdownGroup.vue.d.ts +17 -0
  47. package/dist/types/src/components/UiDropdown/UiDropdownItem.vue.d.ts +9 -4
  48. package/dist/types/src/components/UiDropdown/UiDropdownTrigger.vue.d.ts +21 -5
  49. package/dist/types/src/components/UiDropdown/index.d.ts +1 -0
  50. package/dist/types/src/components/UiEmojiPickerList/UiEmojiPickerList.vue.d.ts +22 -0
  51. package/dist/types/src/components/UiEmptyState/UiEmptyState.vue.d.ts +8 -3
  52. package/dist/types/src/components/UiFieldWrapper/UiFieldWrapper.vue.d.ts +2 -2
  53. package/dist/types/src/components/UiInputCheckbox/CheckboxGlyph.vue.d.ts +17 -0
  54. package/dist/types/src/components/UiInputCheckbox/UiInputCheckbox.vue.d.ts +35 -4
  55. package/dist/types/src/components/UiInputEmoji/UiInputEmoji.vue.d.ts +45 -0
  56. package/dist/types/src/components/UiInputRadio/RadioGlyph.vue.d.ts +19 -0
  57. package/dist/types/src/components/UiInputRadio/UiInputRadio.vue.d.ts +80 -0
  58. package/dist/types/src/components/UiInputRadio/UiInputRadioGroup.vue.d.ts +86 -0
  59. package/dist/types/src/components/UiInputRadio/types.d.ts +19 -0
  60. package/dist/types/src/components/UiInputSelect/UiInputSelect.vue.d.ts +5 -5
  61. package/dist/types/src/components/UiInputTextarea/UiInputTextarea.vue.d.ts +5 -5
  62. package/dist/types/src/components/UiListItem/UiListItem.vue.d.ts +18 -4
  63. package/dist/types/src/components/UiLoader/UiLoader.vue.d.ts +6 -5
  64. package/dist/types/src/components/UiLoader/UiSpinnerIcon.vue.d.ts +3 -0
  65. package/dist/types/src/components/UiLogo/UiLogo.vue.d.ts +9 -3
  66. package/dist/types/src/components/UiMenu/UiMenuContent.vue.d.ts +15 -2
  67. package/dist/types/src/components/UiMenu/UiMenuItem.vue.d.ts +9 -3
  68. package/dist/types/src/components/UiNestedOptions/UiNestedOptions.vue.d.ts +13 -0
  69. package/dist/types/src/components/UiPage/UiPage.vue.d.ts +106 -8
  70. package/dist/types/src/components/UiPagination/UiPagination.vue.d.ts +36 -0
  71. package/dist/types/src/components/UiPagination/index.d.ts +2 -0
  72. package/dist/types/src/components/UiPagination/types.d.ts +4 -0
  73. package/dist/types/src/components/UiPane/UiPane.vue.d.ts +22 -0
  74. package/dist/types/src/components/UiPane/UiPaneContent.vue.d.ts +75 -0
  75. package/dist/types/src/components/UiPane/UiPaneTrigger.vue.d.ts +14 -0
  76. package/dist/types/src/components/UiPane/index.d.ts +5 -0
  77. package/dist/types/src/components/UiProgress/UiProgress.vue.d.ts +1 -1
  78. package/dist/types/src/components/UiRating/UiRating.vue.d.ts +62 -0
  79. package/dist/types/src/components/UiRating/types.d.ts +1 -0
  80. package/dist/types/src/components/UiRoomButton/UiRoomButton.vue.d.ts +5 -3
  81. package/dist/types/src/components/UiRoomCountdown/UiRoomCountdown.vue.d.ts +32 -0
  82. package/dist/types/src/components/UiShell/UiShell.vue.d.ts +3 -0
  83. package/dist/types/src/components/UiSideMenu/UiSideMenu.vue.d.ts +22 -1
  84. package/dist/types/src/components/UiSideMenu/UiSideMenuItemContent.vue.d.ts +2 -2
  85. package/dist/types/src/components/UiSwitch/UiSwitch.vue.d.ts +114 -0
  86. package/dist/types/src/components/UiSwitch/types.d.ts +3 -0
  87. package/dist/types/src/components/UiTable/UiTable.vue.d.ts +13 -4
  88. package/dist/types/src/components/UiTable/UiTableActionsCell.vue.d.ts +49 -0
  89. package/dist/types/src/components/UiTable/UiTableActionsHeaderCell.vue.d.ts +3 -0
  90. package/dist/types/src/components/UiTable/UiTableBody.vue.d.ts +11 -1
  91. package/dist/types/src/components/UiTable/UiTableCell.vue.d.ts +16 -1
  92. package/dist/types/src/components/UiTable/UiTableHeaderCell.vue.d.ts +31 -2
  93. package/dist/types/src/components/UiTable/UiTableRow.vue.d.ts +32 -2
  94. package/dist/types/src/components/UiTable/context.d.ts +3 -0
  95. package/dist/types/src/components/UiTable/index.d.ts +4 -0
  96. package/dist/types/src/components/UiTabs/UiTabs.vue.d.ts +8 -2
  97. package/dist/types/src/components/UiTag/UiTag.types.d.ts +25 -0
  98. package/dist/types/src/components/UiTag/UiTag.vue.d.ts +5 -26
  99. package/dist/types/src/components/UiTag/index.d.ts +2 -1
  100. package/dist/types/src/components/UiTextField/UiTextField.vue.d.ts +10 -5
  101. package/dist/types/src/components/UiTooltip/UiTooltipProvider.vue.d.ts +1 -0
  102. package/dist/types/src/components/UiTooltip/UiTooltipTrigger.vue.d.ts +2 -2
  103. package/dist/types/src/composables/storedThemingApplies.d.ts +17 -0
  104. package/dist/types/src/composables/useButtonStyles.d.ts +1 -0
  105. package/dist/types/src/composables/useChoiceboxSurface.d.ts +11 -0
  106. package/dist/types/src/composables/useContentMode.d.ts +23 -0
  107. package/dist/types/src/composables/useControlModes.d.ts +7 -0
  108. package/dist/types/src/composables/useControlShape.d.ts +24 -0
  109. package/dist/types/src/composables/useFieldFocusEvents.d.ts +12 -0
  110. package/dist/types/src/composables/useIconSize.d.ts +13 -0
  111. package/dist/types/src/composables/usePullGesture.d.ts +42 -0
  112. package/dist/types/src/composables/useThemeAndModes.d.ts +4 -0
  113. package/dist/types/src/constants/sizes.d.ts +1 -1
  114. package/dist/types/src/theming/colorScale.d.ts +165 -0
  115. package/dist/types/src/theming/legacyColorScale.d.ts +27 -0
  116. package/dist/types/src/theming/presetColors.d.ts +11 -0
  117. package/dist/types/src/theming/primitiveTokens.d.ts +2 -0
  118. package/dist/types/src/utils/effectiveOpacity.d.ts +2 -0
  119. package/dist/types/src/utils/numbers.d.ts +1 -2
  120. package/dist/types/src/utils/tokenColor.d.ts +2 -0
  121. package/dist/ui-vue.css +2 -1
  122. package/dist/utils.mjs +165 -194
  123. package/index.d.ts +22 -0
  124. package/index.js +44 -0
  125. package/package.json +28 -20
  126. package/src/components/Toast/LayoutToasts.vue +19 -11
  127. package/src/components/Toast/ToastMessage.vue +130 -73
  128. package/src/components/Toast/toastDSV2.ts +238 -67
  129. package/src/components/UiActionBar/UiActionBar.vue +15 -5
  130. package/src/components/UiActionBar/UiActionBarItem.vue +65 -38
  131. package/src/components/UiActionBar/types.ts +16 -1
  132. package/src/components/UiAvatar/UiAvatar.vue +9 -3
  133. package/src/components/UiAvatarList/UiAvatarList.vue +51 -20
  134. package/src/components/UiBadge/UiBadge.vue +1 -1
  135. package/src/components/UiBanner/UiBanner.vue +77 -27
  136. package/src/components/UiBreadcrumb/UiBreadcrumb.vue +1 -1
  137. package/src/components/UiButton/UiButton.vue +96 -29
  138. package/src/components/UiButtonGroup/UiButtonGroup.vue +22 -16
  139. package/src/components/UiButtonGroup/UiButtonGroupItem.vue +44 -14
  140. package/src/components/UiButtonGroup/types.ts +20 -1
  141. package/src/components/UiCard/UiCard.vue +20 -23
  142. package/src/components/UiCarousel/UiCarousel.vue +2 -2
  143. package/src/components/UiChoice/UiChoice.vue +187 -0
  144. package/src/components/UiChoice/index.ts +2 -0
  145. package/src/components/UiChoice/types.ts +11 -0
  146. package/src/components/UiColorField/UiColorField.vue +2 -2
  147. package/src/components/UiColorPicker/UiColorPicker.vue +16 -13
  148. package/src/components/UiCombobox/UiCombobox.types.ts +52 -0
  149. package/src/components/UiCombobox/UiCombobox.vue +296 -0
  150. package/src/components/UiCombobox/index.ts +2 -0
  151. package/src/components/UiDataTable/UiDataTable.types.ts +47 -0
  152. package/src/components/UiDataTable/UiDataTable.vue +422 -0
  153. package/src/components/UiDataTable/index.ts +12 -0
  154. package/src/components/UiDialog/UiDialogClose.vue +1 -1
  155. package/src/components/UiDialog/UiDialogContent.vue +22 -7
  156. package/src/components/UiDrawer/UiDrawer.vue +16 -0
  157. package/src/components/UiDrawer/UiDrawerContent.vue +42 -0
  158. package/src/components/UiDrawer/UiDrawerTrigger.vue +17 -0
  159. package/src/components/UiDropdown/DropdownItemCheckbox.vue +10 -0
  160. package/src/components/UiDropdown/UiDropdownContent.vue +32 -21
  161. package/src/components/UiDropdown/UiDropdownGroup.vue +23 -0
  162. package/src/components/UiDropdown/UiDropdownItem.vue +45 -18
  163. package/src/components/UiDropdown/UiDropdownSeparator.vue +1 -1
  164. package/src/components/UiDropdown/UiDropdownTrigger.vue +139 -45
  165. package/src/components/UiDropdown/index.ts +1 -0
  166. package/src/components/UiEmojiPickerList/UiEmojiPickerList.vue +122 -0
  167. package/src/components/UiEmptyState/UiEmptyState.vue +16 -4
  168. package/src/components/UiFieldWrapper/UiFieldWrapper.vue +10 -4
  169. package/src/components/UiHorizontalSeparator/UiHorizontalSeparator.vue +2 -2
  170. package/src/components/UiInputCheckbox/CheckboxGlyph.vue +78 -0
  171. package/src/components/UiInputCheckbox/UiInputCheckbox.vue +213 -177
  172. package/src/components/UiInputEmoji/UiInputEmoji.vue +96 -0
  173. package/src/components/UiInputRadio/RadioGlyph.vue +45 -0
  174. package/src/components/UiInputRadio/UiInputRadio.vue +382 -0
  175. package/src/components/UiInputRadio/UiInputRadioGroup.vue +166 -0
  176. package/src/components/UiInputRadio/types.ts +24 -0
  177. package/src/components/UiInputSelect/UiInputSelect.vue +9 -7
  178. package/src/components/UiInputTextarea/UiInputTextarea.vue +20 -35
  179. package/src/components/UiLabel/UiLabel.vue +1 -1
  180. package/src/components/UiListItem/UiListItem.vue +55 -9
  181. package/src/components/UiLoader/UiLoader.vue +11 -29
  182. package/src/components/UiLoader/UiSpinnerIcon.vue +21 -0
  183. package/src/components/UiLogo/UiLogo.vue +41 -7
  184. package/src/components/UiMenu/UiMenuContent.vue +24 -3
  185. package/src/components/UiMenu/UiMenuGroup.vue +1 -1
  186. package/src/components/UiMenu/UiMenuItem.vue +14 -3
  187. package/src/components/UiMenu/UiMenuSubContent.vue +1 -1
  188. package/src/components/UiMenu/UiMenuSubTrigger.vue +2 -2
  189. package/src/components/UiNestedOptions/UiNestedOptions.vue +6 -0
  190. package/src/components/UiPage/UiPage.vue +273 -47
  191. package/src/components/UiPageHeader/UiPageHeader.vue +29 -9
  192. package/src/components/UiPagination/UiPagination.vue +99 -0
  193. package/src/components/UiPagination/index.ts +2 -0
  194. package/src/components/UiPagination/types.ts +28 -0
  195. package/src/components/UiPane/UiPane.vue +20 -0
  196. package/src/components/UiPane/UiPaneContent.vue +232 -0
  197. package/src/components/UiPane/UiPaneTrigger.vue +14 -0
  198. package/src/components/UiPane/index.ts +12 -0
  199. package/src/components/UiPopover/UiPopoverContent.vue +21 -1
  200. package/src/components/UiProgress/UiProgress.vue +2 -2
  201. package/src/components/UiRating/UiRating.vue +258 -0
  202. package/src/components/UiRating/types.ts +1 -0
  203. package/src/components/UiRoomButton/UiRoomButton.vue +14 -45
  204. package/src/components/UiRoomCountdown/UiRoomCountdown.vue +376 -0
  205. package/src/components/UiShell/UiShell.vue +10 -4
  206. package/src/components/UiSideMenu/UiSideMenu.vue +158 -18
  207. package/src/components/UiSideMenu/UiSideMenuItemContent.vue +22 -18
  208. package/src/components/UiStepper/UiStepper.vue +3 -16
  209. package/src/components/UiStepper/UiStepperDescription.vue +1 -13
  210. package/src/components/UiStepper/UiStepperIndicator.vue +17 -57
  211. package/src/components/UiStepper/UiStepperItem.vue +1 -8
  212. package/src/components/UiStepper/UiStepperSeparator.vue +5 -16
  213. package/src/components/UiStepper/UiStepperTitle.vue +1 -10
  214. package/src/components/UiSwitch/UiSwitch.vue +375 -0
  215. package/src/components/UiSwitch/types.ts +5 -0
  216. package/src/components/UiTable/UiTable.vue +98 -8
  217. package/src/components/UiTable/UiTableActionsCell.vue +135 -0
  218. package/src/components/UiTable/UiTableActionsHeaderCell.vue +19 -0
  219. package/src/components/UiTable/UiTableBody.vue +16 -1
  220. package/src/components/UiTable/UiTableCell.vue +33 -16
  221. package/src/components/UiTable/UiTableHeader.vue +6 -5
  222. package/src/components/UiTable/UiTableHeaderCell.vue +61 -9
  223. package/src/components/UiTable/UiTableRow.vue +46 -10
  224. package/src/components/UiTable/context.ts +5 -0
  225. package/src/components/UiTable/index.ts +4 -0
  226. package/src/components/UiTabs/UiTabs.vue +61 -63
  227. package/src/components/UiTag/UiTag.types.ts +28 -0
  228. package/src/components/UiTag/UiTag.vue +24 -42
  229. package/src/components/UiTag/index.ts +2 -0
  230. package/src/components/UiTextField/UiTextField.vue +27 -47
  231. package/src/components/UiTimeline/README.md +6 -6
  232. package/src/components/UiTimeline/UiTimelineItem.vue +3 -3
  233. package/src/components/UiTooltip/UiTooltipContent.vue +1 -1
  234. package/src/components/UiTooltip/UiTooltipProvider.vue +3 -0
  235. package/src/components/UiTooltip/UiTooltipTrigger.vue +16 -1
  236. package/src/composables/storedThemingApplies.ts +19 -0
  237. package/src/composables/useButtonStyles.ts +9 -13
  238. package/src/composables/useChoiceboxSurface.ts +36 -0
  239. package/src/composables/useContentMode.ts +66 -0
  240. package/src/composables/useControlModes.ts +11 -0
  241. package/src/composables/useControlShape.ts +34 -0
  242. package/src/composables/useFieldFocusEvents.ts +30 -0
  243. package/src/composables/useIconSize.ts +43 -0
  244. package/src/composables/usePullGesture.ts +142 -0
  245. package/src/composables/useThemeAndModes.ts +28 -1
  246. package/src/constants/sizes.ts +1 -1
  247. package/src/css/body.css +16 -1
  248. package/src/css/fonts.css +39 -5
  249. package/src/global.d.ts +44 -0
  250. package/src/theming/colorScale.ts +173 -0
  251. package/src/theming/legacyColorScale.ts +58 -0
  252. package/src/theming/presetColors.ts +33 -0
  253. package/src/theming/primitiveTokens.ts +71 -0
  254. package/src/utils/effectiveOpacity.ts +8 -0
  255. package/src/utils/i18n.ts +6 -0
  256. package/src/utils/numbers.ts +1 -1
  257. package/src/utils/responsive.d.ts +14 -0
  258. package/src/utils/tokenColor.ts +9 -0
  259. package/dist/UiTimelineItem.vue_vue_type_style_index_0_lang-DJFdCn2R.mjs +0 -5456
  260. package/dist/a11y-BhF_nl7L.mjs +0 -159
  261. package/dist/i18n-Cktw9hIu.mjs +0 -49
  262. package/dist/toastDSV2-wMh-UMzW.mjs +0 -74
  263. package/dist/types/src/components/UiAvatar/index.d.ts +0 -1
  264. package/dist/types/src/components/UiAvatarList/index.d.ts +0 -1
  265. package/dist/types/src/components/UiBadge/index.d.ts +0 -1
  266. package/dist/types/src/components/UiBanner/index.d.ts +0 -2
  267. package/dist/types/src/components/UiButtonGroup/index.d.ts +0 -3
  268. package/dist/types/src/components/UiCarousel/index.d.ts +0 -2
  269. package/dist/types/src/components/UiColorField/index.d.ts +0 -2
  270. package/dist/types/src/components/UiDialog/MarketplaceHeaderExample.vue.d.ts +0 -28
  271. package/dist/types/src/components/UiDialog/index.d.ts +0 -7
  272. package/dist/types/src/components/UiFieldWrapper/index.d.ts +0 -1
  273. package/dist/types/src/components/UiInputCheckbox/index.d.ts +0 -2
  274. package/dist/types/src/components/UiInputSelect/index.d.ts +0 -2
  275. package/dist/types/src/components/UiInputTextarea/index.d.ts +0 -2
  276. package/dist/types/src/components/UiMenu/index.d.ts +0 -10
  277. package/dist/types/src/components/UiProgress/index.d.ts +0 -2
  278. package/dist/types/src/components/UiTabs/index.d.ts +0 -2
  279. package/dist/types/src/components/UiTimeline/index.d.ts +0 -2
  280. package/dist/types/src/components/UiTooltip/index.d.ts +0 -4
  281. package/dist/types/src/mixins/DynamicAction.d.ts +0 -30
  282. package/dist/useThemeAndModes-BzYwSOEv.mjs +0 -108
  283. package/src/components/UiAvatar/index.js +0 -1
  284. package/src/components/UiAvatarList/index.js +0 -1
  285. package/src/components/UiBadge/index.ts +0 -2
  286. package/src/components/UiBanner/index.ts +0 -2
  287. package/src/components/UiButtonGroup/index.ts +0 -3
  288. package/src/components/UiCarousel/index.ts +0 -2
  289. package/src/components/UiColorField/index.ts +0 -2
  290. package/src/components/UiDialog/MarketplaceHeaderExample.vue +0 -217
  291. package/src/components/UiDialog/index.ts +0 -18
  292. package/src/components/UiFieldWrapper/index.ts +0 -1
  293. package/src/components/UiInputCheckbox/index.ts +0 -2
  294. package/src/components/UiInputSelect/index.ts +0 -2
  295. package/src/components/UiInputTextarea/index.ts +0 -2
  296. package/src/components/UiMenu/index.ts +0 -10
  297. package/src/components/UiProgress/index.ts +0 -2
  298. package/src/components/UiTabs/index.ts +0 -5
  299. package/src/components/UiTag/index.js +0 -1
  300. package/src/components/UiTimeline/index.ts +0 -2
  301. package/src/components/UiTooltip/index.js +0 -4
  302. package/src/mixins/DynamicAction.js +0 -61
@@ -1,12 +1,20 @@
1
- import { ref } from 'vue'
2
- import type { Component } from 'vue'
1
+ import { markRaw, ref } from 'vue'
2
+ import type { Component, Ref } from 'vue'
3
3
  import type { RouteLocationRaw } from 'vue-router'
4
4
  import { createEventHook, type EventHook } from '@vueuse/core'
5
5
 
6
6
  import type { ThemeColorType } from '../../theme'
7
+ import type { Size } from '../../constants'
7
8
 
8
9
  let uid = 1
9
10
 
11
+ /**
12
+ * How many toasts render at once. It lives here rather than in `LayoutToasts`
13
+ * because the store schedules the close timers, and only the toasts actually
14
+ * on screen may run one.
15
+ */
16
+ export const MAX_ITEMS_DISPLAYED = 3
17
+
10
18
  export type ToastType = 'alert'|'warning'|'info'|'success'
11
19
 
12
20
  const DEFAULT_TIMER_DURATION_PER_TYPE: Record<Extract<ToastType, 'success'|'info'>, number> = {
@@ -14,14 +22,39 @@ const DEFAULT_TIMER_DURATION_PER_TYPE: Record<Extract<ToastType, 'success'|'info
14
22
  info: 7 * 1000,
15
23
  }
16
24
 
17
- export interface ToastOptions {
18
- message: string
25
+ /**
26
+ * Identity shown in place of the status icon. Size and variant are fixed by
27
+ * the design, so only the identity-bearing `UiAvatar` props are exposed.
28
+ */
29
+ export interface ToastAvatar {
30
+ src?: string | null
31
+ firstName?: string | null
32
+ lastName?: string | null
33
+ customText?: string | null
34
+ color?: string | null
35
+ alt?: string
36
+ }
37
+
38
+ /**
39
+ * Every field a toast understands, all optional. `openToast` narrows this to
40
+ * `ToastOptions`, which additionally demands something to display.
41
+ */
42
+ export interface ToastBaseOptions {
43
+ message?: string
44
+ /**
45
+ * Rendered in place of `message`, inside the default layout — `component`
46
+ * replaces the whole body instead. Markup, and sanitizing it, stays with
47
+ * the caller.
48
+ */
49
+ messageComponent?: Component
50
+ messageComponentProps?: Record<string, unknown>
19
51
  id?: PropertyKey
20
52
  title?: string
21
53
  info?: string
22
54
  type?: ThemeColorType
55
+ avatar?: ToastAvatar
23
56
  time?: number | boolean // if time is explicitly false, the toast will not auto-close
24
- progessIndeterminate?: boolean
57
+ progressIndeterminate?: boolean
25
58
  customIcon?: string
26
59
  confirmLabel?: string
27
60
  confirmHref?: string
@@ -29,6 +62,7 @@ export interface ToastOptions {
29
62
  confirmTo?: RouteLocationRaw
30
63
  confirmIcon?: string
31
64
  confirmColor?: ThemeColorType
65
+ actionsSize?: Size
32
66
  cancelLabel?: string
33
67
  locked?: boolean
34
68
  closable?: boolean
@@ -36,116 +70,253 @@ export interface ToastOptions {
36
70
  componentProps?: Record<string, unknown>
37
71
  }
38
72
 
73
+ /**
74
+ * A toast has to show something. Requiring one of the three content fields
75
+ * here means an empty toast is a type error rather than an empty white box.
76
+ */
77
+ export type ToastContent =
78
+ | { message: string }
79
+ | { messageComponent: Component }
80
+ | { title: string }
81
+ | { component: Component }
82
+
83
+ export type ToastOptions = ToastBaseOptions & ToastContent
84
+
85
+ /**
86
+ * The caller's options as the store keeps them: `time` has been resolved to
87
+ * milliseconds, so nothing downstream has to handle the `true` shorthand.
88
+ */
89
+ type StoredToastOptions = Omit<ToastBaseOptions, 'time'> & { time?: number }
90
+
91
+ /**
92
+ * A toast on screen: what the caller asked for, kept in `options`, plus the
93
+ * bookkeeping the store needs alongside it. They stay apart so `LayoutToasts`
94
+ * can hand the component the options without the timer handle and the event
95
+ * hooks falling through onto the DOM.
96
+ */
39
97
  interface ToastItem {
40
98
  id: PropertyKey
41
- message: string
42
- title?: string
43
- info?: string
44
- type?: ThemeColorType
45
- icon?: string
46
- time?: number
47
- locked?: boolean
48
- progessIndeterminate?: boolean
99
+ options: StoredToastOptions
49
100
  closeTimer: any
101
+ // The pointer is resting on this toast, so it must not be given a timer
102
+ // back when the visible window is recomputed.
103
+ paused: boolean
104
+ // When the current run of the close timer started. The progress bar animation
105
+ // is component state, so without this it has no way to observe a restart.
106
+ timerStartedAt: number
50
107
  confirmEventHook: EventHook
51
108
  cancelEventHook: EventHook
52
109
  }
53
110
 
54
- const toasts = ref<ToastItem[]>([])
111
+ /**
112
+ * `true` picks the per-type default; a number is used as-is; `false` (or
113
+ * unset) means no auto-close — normalized to undefined so the timer is never
114
+ * started, and so nothing downstream has to divide a boolean by 1000.
115
+ */
116
+ function resolveTime(time: ToastBaseOptions['time'], type: ThemeColorType): number | undefined {
117
+ const typeWithTimer = type === 'success' || type === 'info'
118
+
119
+ return time === true
120
+ ? (typeWithTimer ? DEFAULT_TIMER_DURATION_PER_TYPE[type] : undefined)
121
+ : (typeof time === 'number' ? time : undefined)
122
+ }
123
+
124
+ // Cast past `UnwrapRef`, which mangles the `Component` in `options` into a
125
+ // shape that no longer matches ToastItem. Nothing here holds a nested ref, so
126
+ // the unwrapped type and the declared one are the same at runtime.
127
+ const toasts = ref<ToastItem[]>([]) as Ref<ToastItem[]>
55
128
 
56
- export function useToasts () {
129
+ export function useToasts() {
57
130
  return {
58
131
  toasts,
59
- closeToast,
60
132
  openToast,
133
+ updateToast,
134
+ restartToast,
135
+ closeToast,
61
136
  cancelCloseTimer,
62
137
  }
63
138
  }
64
139
 
65
- export function openToast (options: ToastOptions) {
66
- let id: PropertyKey
67
- let item = toasts.value.find(item => item.id === options.id)
68
- const confirmEventHook = createEventHook<void>()
69
- const cancelEventHook = createEventHook<void>()
70
-
71
- if (item) {
72
- id = item.id
73
- Object.assign(item, {
74
- ...options,
75
- })
76
-
77
- const restartTimer = options.id && Object.keys(options).length === 1
78
- if (restartTimer) {
79
- startTimer(item)
80
- }
81
- } else {
82
- id = options.id ?? uid++
83
-
84
- const type = options.type || 'info'
85
- const typeWithTimer = type === 'success' || type === 'info'
86
- // `true` picks the per-type default; a number is used as-is; `false`
87
- // (or unset) means no auto-close — normalized to undefined so the timer
88
- // is never started.
89
- const time = options.time === true
90
- ? (typeWithTimer ? DEFAULT_TIMER_DURATION_PER_TYPE[type] : undefined)
91
- : (typeof options.time === 'number' ? options.time : undefined)
92
- item = {
93
- ...options,
94
- id,
95
- closeTimer: null,
96
- confirmEventHook,
97
- cancelEventHook,
98
- time,
99
- type,
100
- }
101
-
102
- toasts.value.push(item)
140
+ /** What a caller gets back for the toast it just opened or patched. */
141
+ interface ToastHandle {
142
+ id: PropertyKey
143
+ close: () => void
144
+ onConfirm: EventHook['on']
145
+ onCancel: EventHook['on']
146
+ }
103
147
 
104
- startTimer(item)
105
- }
148
+ function toastHandle(item: ToastItem): ToastHandle {
149
+ const { id } = item
106
150
 
107
151
  return {
108
152
  id,
109
153
  close: () => {
110
154
  closeToast(id)
111
155
  },
112
- onConfirm: confirmEventHook.on,
113
- onCancel: cancelEventHook.on,
156
+ // Read off the item, so a patched toast hands back the hooks it was
157
+ // opened with rather than a fresh pair nothing would ever trigger.
158
+ onConfirm: item.confirmEventHook.on,
159
+ onCancel: item.cancelEventHook.on,
160
+ }
161
+ }
162
+
163
+ /**
164
+ * The store is a deep ref, so a component definition kept in it would be
165
+ * proxied on every render — Vue warns about exactly that.
166
+ */
167
+ function withRawComponents<T extends ToastBaseOptions>(options: T): T {
168
+ const { component, messageComponent } = options
169
+
170
+ return {
171
+ ...options,
172
+ ...(component && { component: markRaw(component) }),
173
+ ...(messageComponent && { messageComponent: markRaw(messageComponent) }),
114
174
  }
115
175
  }
116
176
 
117
- export function closeToast (id: PropertyKey) {
177
+ function applyPatch(item: ToastItem, patch: Omit<ToastBaseOptions, 'id'>) {
178
+ const { time, ...rest } = patch
179
+ Object.assign(item.options, withRawComponents(rest))
180
+
181
+ // Patching goes through the same normalization as opening, so the store only
182
+ // ever holds milliseconds and the progress bar never divides a boolean.
183
+ if (time !== undefined) {
184
+ item.options.time = resolveTime(time, item.options.type ?? 'info')
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Opens a toast. An `id` is an identity, so opening one that is already on
190
+ * screen patches it in place instead of stacking a second copy — that is what
191
+ * the room's per-user and per-link toasts rely on. It does not restart the
192
+ * close timer; call `restartToast` for that.
193
+ */
194
+ export function openToast(options: ToastOptions): ToastHandle {
195
+ const open = toasts.value.find(item => item.id === options.id)
196
+ if (open) {
197
+ // Re-opening is a fresh occurrence of the same identity, and the caller is
198
+ // about to bind the handlers for it. Without dropping the previous ones,
199
+ // every re-open leaves another subscriber on hooks nothing ever clears.
200
+ open.confirmEventHook.clear()
201
+ open.cancelEventHook.clear()
202
+ applyPatch(open, options)
203
+
204
+ return toastHandle(open)
205
+ }
206
+
207
+ const type = options.type || 'info'
208
+ const id = options.id ?? uid++
209
+ const item: ToastItem = {
210
+ id,
211
+ options: {
212
+ ...withRawComponents(options),
213
+ id,
214
+ type,
215
+ time: resolveTime(options.time, type),
216
+ },
217
+ closeTimer: null,
218
+ paused: false,
219
+ timerStartedAt: 0,
220
+ // Created with the toast, not on every call, so the hooks outlive a patch.
221
+ confirmEventHook: createEventHook<void>(),
222
+ cancelEventHook: createEventHook<void>(),
223
+ }
224
+
225
+ toasts.value.push(item)
226
+ syncTimers()
227
+
228
+ return toastHandle(item)
229
+ }
230
+
231
+ /**
232
+ * Patches an open toast. Unlike `openToast` it needs no content, so a caller
233
+ * can change one field. Returns undefined if the toast has already closed.
234
+ */
235
+ export function updateToast(id: PropertyKey, patch: Omit<ToastBaseOptions, 'id'>): ToastHandle | undefined {
236
+ const item = toasts.value.find(item => item.id === id)
237
+ if (!item) return
238
+
239
+ applyPatch(item, patch)
240
+
241
+ return toastHandle(item)
242
+ }
243
+
244
+ /** Runs the close timer again from zero, e.g. when the pointer leaves. */
245
+ export function restartToast(id: PropertyKey) {
246
+ const item = toasts.value.find(item => item.id === id)
247
+ if (!item) return
248
+
249
+ item.paused = false
250
+ startTimer(item)
251
+ }
252
+
253
+ export function closeToast(id: PropertyKey) {
118
254
  const item = toasts.value.find(item => item.id === id)
119
255
  if (item) {
120
- clearTimeout(item.closeTimer)
256
+ stopTimer(item)
121
257
  removeToast(id)
258
+ // Closing one frees a slot, so whatever was queued behind it can start
259
+ // counting down now that it is on screen.
260
+ syncTimers()
122
261
  }
123
262
  }
124
263
 
125
- export function cancelCloseTimer (id: PropertyKey) {
264
+ export function cancelCloseTimer(id: PropertyKey) {
126
265
  const item = toasts.value.find(item => item.id === id)
127
266
  if (item) {
128
- clearTimeout(item.closeTimer)
267
+ item.paused = true
268
+ stopTimer(item)
129
269
  }
130
270
  }
131
271
 
132
- export function resetToasts () {
272
+ export function resetToasts() {
273
+ toasts.value.forEach(stopTimer)
133
274
  toasts.value = []
134
275
  }
135
276
 
136
- function removeToast (id: PropertyKey) {
277
+ function removeToast(id: PropertyKey) {
137
278
  const index = toasts.value.findIndex(item => item.id === id)
138
279
  if (index !== -1) {
139
280
  toasts.value.splice(index, 1)
140
281
  }
141
282
  }
142
283
 
143
- function startTimer (item: ToastItem): void {
144
- if (item.time) {
284
+ /**
285
+ * Gives a close timer to every toast on screen and takes it away from the ones
286
+ * queued behind them. Without this a queued toast counts down while nothing is
287
+ * rendered and can be removed before it is ever seen.
288
+ */
289
+ function syncTimers(): void {
290
+ toasts.value.forEach((item, index) => {
291
+ const onScreen = index < MAX_ITEMS_DISPLAYED
292
+
293
+ if (!onScreen || item.paused) {
294
+ stopTimer(item)
295
+ return
296
+ }
297
+
298
+ // Guarded so resettling the window never restarts a countdown that is
299
+ // already running, which would also replay the progress bar.
300
+ if (!item.closeTimer) startTimer(item)
301
+ })
302
+ }
303
+
304
+ function stopTimer(item: ToastItem): void {
305
+ clearTimeout(item.closeTimer)
306
+ item.closeTimer = null
307
+ }
308
+
309
+ function startTimer(item: ToastItem): void {
310
+ // Idempotent: without this a restart would orphan the previous timeout,
311
+ // which would go on to close the toast early.
312
+ stopTimer(item)
313
+
314
+ if (item.options.time) {
315
+ item.timerStartedAt = performance.now()
145
316
  item.closeTimer = setTimeout(
146
317
  () => {
147
318
  closeToast(item.id)
148
- }, item.time
319
+ }, item.options.time
149
320
  )
150
321
  }
151
322
  }
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, provide } from 'vue'
3
- import type { ActionBarOrientation, ActionBarPosition, ActionBarSize } from './types'
4
- import { ACTION_BAR_CONTEXT_KEY } from './types'
3
+ import type { ActionBarOrientation, ActionBarPosition, ActionBarSize, ActionBarTooltipSide } from './types'
4
+ import { ACTION_BAR_CONTEXT_KEY, ACTION_BAR_RAIL_RADIUS } from './types'
5
5
 
6
6
  interface UiActionBarProps {
7
7
  /**
@@ -26,6 +26,7 @@ interface UiActionBarProps {
26
26
  * Accessible label for the toolbar landmark. Defaults to "Actions".
27
27
  */
28
28
  ariaLabel?: string
29
+ tooltipSide?: ActionBarTooltipSide
29
30
  }
30
31
 
31
32
  const props = withDefaults(defineProps<UiActionBarProps>(), {
@@ -34,17 +35,26 @@ const props = withDefaults(defineProps<UiActionBarProps>(), {
34
35
  position: undefined,
35
36
  offset: 3,
36
37
  ariaLabel: 'Actions',
38
+ tooltipSide: undefined,
37
39
  })
38
40
 
39
41
  provide(ACTION_BAR_CONTEXT_KEY, {
40
42
  orientation: computed<ActionBarOrientation>(() => props.orientation),
41
43
  size: computed<ActionBarSize>(() => props.size),
44
+ tooltipSide: computed<ActionBarTooltipSide | undefined>(() => props.tooltipSide),
42
45
  })
43
46
 
44
47
  const isVertical = computed(() => props.orientation === 'vertical')
45
48
 
46
49
  // Inner padding follows the design tokens (space-0-5 = 2px).
47
- const sizeClasses = computed(() => (props.size === 'sm' ? 'p-0.5 gap-0.5' : 'p-1 gap-0.5'))
50
+ const SIZE_CLASSES: Record<ActionBarSize, string> = {
51
+ xs: 'p-0.5 gap-0.5',
52
+ sm: 'p-0.5 gap-0.5',
53
+ md: 'p-1 gap-0.5',
54
+ }
55
+
56
+ const sizeClasses = computed(() => SIZE_CLASSES[props.size])
57
+ const railRadiusClass = computed(() => ACTION_BAR_RAIL_RADIUS[props.size])
48
58
 
49
59
  // When `position` is set, anchor the bar to an edge of its positioned
50
60
  // ancestor. Offsets are inline styles (rem) so any `offset` value works
@@ -77,8 +87,8 @@ const positionStyle = computed<Record<string, string>>(() => {
77
87
  :aria-label="ariaLabel"
78
88
  :aria-orientation="orientation"
79
89
  data-testid="ui-action-bar"
80
- class="inline-flex items-center bg-surface-neutral-bg-main rounded-xl border border-borders-neutral-light shadow-lg"
81
- :class="[sizeClasses, { 'flex-col': isVertical, 'absolute z-10': position }]"
90
+ class="inline-flex items-center bg-surface-neutral-bg-main border border-container-border shadow-lg"
91
+ :class="[sizeClasses, railRadiusClass, { 'flex-col': isVertical, 'absolute z-10': position }]"
82
92
  :style="positionStyle"
83
93
  >
84
94
  <slot />
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { computed, inject } from 'vue'
2
+ import { computed, inject, useAttrs } from 'vue'
3
3
  import {
4
4
  TooltipProvider,
5
5
  TooltipRoot,
@@ -9,16 +9,21 @@ import {
9
9
  TooltipArrow,
10
10
  } from 'reka-ui'
11
11
  import type { RouteLocationRaw } from 'vue-router'
12
- import { ACTION_BAR_CONTEXT_KEY } from './types'
12
+ import { useIconSize } from '../../composables/useIconSize'
13
+ import type { ActionBarSize } from './types'
14
+ import { ACTION_BAR_CONTEXT_KEY, ACTION_BAR_ITEM_RADIUS } from './types'
13
15
 
14
16
  interface UiActionBarItemProps {
15
- /** Icon class, e.g. `i-ls-trash`. */
17
+ /**
18
+ * Icon class, e.g. `i-ph-trash`. Ignored when the `icon` slot is filled —
19
+ * use the slot for glyphs no icon class covers (emoji, an avatar, a dot).
20
+ */
16
21
  icon?: string
17
22
  /** Visible/accessible label. Required so icon-only items stay accessible. */
18
23
  label: string
19
24
  /**
20
25
  * Optional icon class rendered next to the label inside the hover tooltip —
21
- * e.g. `i-ls-external-link` to flag that the action opens in a new tab.
26
+ * e.g. `i-ph-arrow-square-out` to flag that the action opens in a new tab.
22
27
  * Only shown for icon-only items (the tooltip is suppressed when
23
28
  * `showLabel` is set).
24
29
  */
@@ -55,13 +60,20 @@ const props = withDefaults(defineProps<UiActionBarItemProps>(), {
55
60
 
56
61
  const emit = defineEmits<{ click: [event: MouseEvent] }>()
57
62
 
63
+ // The trigger is rendered through `TooltipTrigger as-child`, which merges its
64
+ // own props over the child vnode — so normal attribute fallthrough never
65
+ // reaches the button. Read the consumer's `data-testid` explicitly, otherwise
66
+ // every item in a wrapped bar would share one indistinguishable test id.
67
+ const attrs = useAttrs()
68
+ const testId = computed(() => (attrs['data-testid'] as string | undefined) ?? 'ui-action-bar-item')
69
+
58
70
  const ctx = inject(ACTION_BAR_CONTEXT_KEY, null)
59
71
  const size = computed(() => ctx?.size.value ?? 'md')
60
72
  const orientation = computed(() => ctx?.orientation.value ?? 'horizontal')
61
73
 
62
74
  // Icon-only items get a tooltip; labeled items don't need one.
63
75
  const showTooltip = computed(() => !props.showLabel)
64
- const tooltipSide = computed(() => (orientation.value === 'vertical' ? 'right' : 'bottom'))
76
+ const tooltipSide = computed(() => ctx?.tooltipSide.value ?? (orientation.value === 'vertical' ? 'right' : 'bottom'))
65
77
 
66
78
  const component = computed(() => {
67
79
  if (props.disabled) return { is: 'button', props: { type: 'button', disabled: true } }
@@ -70,22 +82,33 @@ const component = computed(() => {
70
82
  return { is: 'button', props: { type: 'button' } }
71
83
  })
72
84
 
73
- const sizeClasses = computed(() => {
74
- const square = size.value === 'sm' ? 'size-8' : 'size-10'
75
- // Labeled items grow with their text instead of staying square.
76
- const padded = size.value === 'sm' ? 'h-8 px-2.5 gap-1' : 'h-10 px-3 gap-1.5'
77
- return props.showLabel ? padded : square
78
- })
85
+ // Per-size layout classes. `square` sizes icon-only items; labeled items use
86
+ // `padded` and grow with their text instead of staying square. xs reuses the
87
+ // sm text style — the design system defines no xs one.
88
+ const SIZE_CLASSES: Record<ActionBarSize, { square: string, padded: string, text: string }> = {
89
+ xs: { square: 'size-6', padded: 'h-6 px-2 gap-1', text: 'text-action-button-sm' },
90
+ sm: { square: 'size-8', padded: 'h-8 px-2.5 gap-1', text: 'text-action-button-sm' },
91
+ md: { square: 'size-10', padded: 'h-10 px-3 gap-1.5', text: 'text-action-button-md' },
92
+ }
79
93
 
80
- const iconSizeClass = computed(() => (size.value === 'sm' ? 'size-4' : 'size-5'))
81
- const textClass = computed(() => (size.value === 'sm' ? 'text-action-button-sm' : 'text-action-button-md'))
94
+ const sizeConfig = computed(() => SIZE_CLASSES[size.value])
95
+ const iconSizeClass = useIconSize(size)
82
96
 
83
- // Persistent pressed background when the item is an active toggle. Uses the
84
- // dedicated neutral "active" token so an active-but-not-hovered toggle stays
85
- // visually distinct from a merely hovered item.
86
- const stateClasses = computed(() => ({
87
- 'bg-actions-neutral-active-overlay': props.active === true,
88
- }))
97
+ // Everything bound on the item root: per-size layout and text classes, plus
98
+ // the persistent pressed background when the item is an active toggle (the
99
+ // dedicated neutral "active" token keeps an active-but-not-hovered toggle
100
+ // visually distinct from a merely hovered item).
101
+ const itemClasses = computed(() => [
102
+ props.showLabel ? sizeConfig.value.padded : sizeConfig.value.square,
103
+ sizeConfig.value.text,
104
+ ACTION_BAR_ITEM_RADIUS[size.value],
105
+ // Labeled items in a vertical rail stretch so their icons align.
106
+ props.showLabel && orientation.value === 'vertical' ? 'w-full justify-start' : 'justify-center',
107
+ {
108
+ 'bg-actions-neutral-active-overlay': props.active === true,
109
+ 'cursor-pointer': !props.disabled,
110
+ },
111
+ ])
89
112
 
90
113
  function onClick (event: MouseEvent) {
91
114
  if (props.disabled) {
@@ -113,20 +136,22 @@ function onClick (event: MouseEvent) {
113
136
  v-bind="component.props"
114
137
  :aria-label="label"
115
138
  :aria-pressed="active"
116
- data-testid="ui-action-bar-item"
117
- class="group inline-flex items-center justify-center rounded-lg shrink-0 outline-none transition-colors
139
+ :data-testid="testId"
140
+ class="group inline-flex items-center shrink-0 outline-none transition-colors
118
141
  text-actions-neutral-idle
119
142
  hover:bg-actions-neutral-hover-overlay
120
- focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus-ring
143
+ focus-visible:focus-ring
121
144
  disabled:opacity-50 disabled:cursor-not-allowed"
122
- :class="[sizeClasses, textClass, stateClasses, { 'cursor-pointer': !disabled }]"
145
+ :class="itemClasses"
123
146
  @click="onClick"
124
147
  >
125
- <span
126
- v-if="icon"
127
- :class="[icon, iconSizeClass]"
128
- class="shrink-0"
129
- />
148
+ <slot name="icon">
149
+ <span
150
+ v-if="icon"
151
+ :class="[icon, iconSizeClass]"
152
+ class="shrink-0"
153
+ />
154
+ </slot>
130
155
  </component>
131
156
  </TooltipTrigger>
132
157
  <TooltipPortal>
@@ -134,7 +159,7 @@ function onClick (event: MouseEvent) {
134
159
  :side="tooltipSide"
135
160
  :side-offset="8"
136
161
  data-colors-semantic="light"
137
- class="inline-flex items-center justify-center gap-1.5 py-1.5 px-3 bg-surface-neutral-invert-bg-alpha text-text-neutral-invert-base text-content-text-md-regular rounded-md text-center z-50 shadow-shadows-drop-shadow-lg
162
+ class="inline-flex items-center justify-center gap-1.5 py-1.5 px-3 bg-surface-neutral-invert-bg-alpha text-text-neutral-invert-base text-content-text-md-regular rounded-row text-center z-overlay shadow-shadows-drop-shadow-lg
138
163
  data-[state=delayed-open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=delayed-open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=delayed-open]:zoom-in-95"
139
164
  >
140
165
  {{ label }}
@@ -160,20 +185,22 @@ function onClick (event: MouseEvent) {
160
185
  v-bind="component.props"
161
186
  :aria-label="label"
162
187
  :aria-pressed="active"
163
- data-testid="ui-action-bar-item"
164
- class="group inline-flex items-center justify-center rounded-lg shrink-0 outline-none transition-colors
188
+ :data-testid="testId"
189
+ class="group inline-flex items-center shrink-0 outline-none transition-colors
165
190
  text-actions-neutral-idle
166
191
  hover:bg-actions-neutral-hover-overlay
167
- focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus-ring
192
+ focus-visible:focus-ring
168
193
  disabled:opacity-50 disabled:cursor-not-allowed"
169
- :class="[sizeClasses, textClass, stateClasses, { 'cursor-pointer': !disabled }]"
194
+ :class="itemClasses"
170
195
  @click="onClick"
171
196
  >
172
- <span
173
- v-if="icon"
174
- :class="[icon, iconSizeClass]"
175
- class="shrink-0"
176
- />
197
+ <slot name="icon">
198
+ <span
199
+ v-if="icon"
200
+ :class="[icon, iconSizeClass]"
201
+ class="shrink-0"
202
+ />
203
+ </slot>
177
204
  <span class="whitespace-nowrap">{{ label }}</span>
178
205
  </component>
179
206
  </template>
@@ -1,7 +1,7 @@
1
1
  import type { ComputedRef, InjectionKey } from 'vue'
2
2
 
3
3
  export type ActionBarOrientation = 'horizontal' | 'vertical'
4
- export type ActionBarSize = 'sm' | 'md'
4
+ export type ActionBarSize = 'xs' | 'sm' | 'md'
5
5
  export type ActionBarPosition =
6
6
  | 'top-left'
7
7
  | 'top-right'
@@ -9,10 +9,12 @@ export type ActionBarPosition =
9
9
  | 'center-right'
10
10
  | 'bottom-left'
11
11
  | 'bottom-right'
12
+ export type ActionBarTooltipSide = 'top' | 'right' | 'bottom' | 'left'
12
13
 
13
14
  export interface ActionBarContext {
14
15
  orientation: ComputedRef<ActionBarOrientation>
15
16
  size: ComputedRef<ActionBarSize>
17
+ tooltipSide: ComputedRef<ActionBarTooltipSide | undefined>
16
18
  }
17
19
 
18
20
  /**
@@ -22,3 +24,16 @@ export interface ActionBarContext {
22
24
  */
23
25
  export const ACTION_BAR_CONTEXT_KEY: InjectionKey<ActionBarContext> =
24
26
  Symbol('ui-action-bar-context')
27
+
28
+ export const ACTION_BAR_RAIL_RADIUS: Record<ActionBarSize, string> = {
29
+ xs: 'rounded-button-sm',
30
+ sm: 'rounded-button-sm',
31
+ md: 'rounded-field-lg',
32
+ }
33
+
34
+ // Item radius stays concentric with the rail: rail radius minus rail padding and border.
35
+ export const ACTION_BAR_ITEM_RADIUS: Record<ActionBarSize, string> = {
36
+ xs: 'rounded-[calc(var(--radius-button-sm)_-_3px)]',
37
+ sm: 'rounded-[calc(var(--radius-button-sm)_-_3px)]',
38
+ md: 'rounded-[calc(var(--radius-field-lg)_-_5px)]',
39
+ }