@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
@@ -0,0 +1,45 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue'
3
+ import { RadioGroupItem, RadioGroupIndicator } from 'reka-ui'
4
+ import type { RadioValue } from './types'
5
+
6
+ interface Props {
7
+ inputId: string
8
+ value: RadioValue
9
+ disabled: boolean
10
+ error: boolean
11
+ ariaDescribedby?: string
12
+ radioClasses: string[]
13
+ indicatorClasses: string[]
14
+ }
15
+
16
+ defineProps<Props>()
17
+
18
+ const emit = defineEmits<{
19
+ select: [event: Event]
20
+ }>()
21
+
22
+ const itemRef = ref<InstanceType<typeof RadioGroupItem> | null>(null)
23
+
24
+ defineExpose({
25
+ el: (): HTMLElement | undefined => (itemRef.value?.$el as HTMLElement | undefined) ?? undefined,
26
+ })
27
+ </script>
28
+
29
+ <template>
30
+ <RadioGroupItem
31
+ :id="inputId"
32
+ ref="itemRef"
33
+ :value="value"
34
+ :disabled="disabled"
35
+ :class="radioClasses"
36
+ :aria-invalid="error || undefined"
37
+ :aria-describedby="ariaDescribedby"
38
+ @select="emit('select', $event)"
39
+ >
40
+ <RadioGroupIndicator
41
+ data-testid="ui-input-radio-dot"
42
+ :class="indicatorClasses"
43
+ />
44
+ </RadioGroupItem>
45
+ </template>
@@ -0,0 +1,382 @@
1
+ <script setup lang="ts">
2
+ import { computed, inject, provide, ref } from 'vue'
3
+ import { Label, useId } from 'reka-ui'
4
+ import type { RadioValue } from './types'
5
+ import { RADIO_GROUP_KEY } from './types'
6
+ import { CHOICE_ROW_KEY } from '../UiChoice/types'
7
+ import type { ControlPosition } from '../UiChoice/types'
8
+ import UiTooltip from '../UiTooltip/UiTooltip.vue'
9
+ import RadioGlyph from './RadioGlyph.vue'
10
+
11
+ interface UiInputRadioProps {
12
+ /**
13
+ * The value this option represents. `null` is a valid value;
14
+ * object values must be reference-stable.
15
+ */
16
+ value: RadioValue
17
+ /**
18
+ * The label text
19
+ */
20
+ label?: string
21
+ /**
22
+ * Description text below the label
23
+ */
24
+ description?: string
25
+ /**
26
+ * Info tooltip for the label
27
+ */
28
+ labelInfo?: string
29
+ /**
30
+ * Icon class rendered between the radio and the text (e.g. `i-ph-bell`).
31
+ * In vertical orientation it sits above the text instead.
32
+ */
33
+ icon?: string
34
+ /**
35
+ * Whether this option is disabled
36
+ * @default false
37
+ */
38
+ disabled?: boolean
39
+ /**
40
+ * Layout orientation of the radio relative to the label
41
+ * @default 'horizontal'
42
+ */
43
+ orientation?: 'horizontal' | 'vertical'
44
+ /**
45
+ * Which side of the text the radio sits on, in horizontal orientation
46
+ * @default 'start'
47
+ */
48
+ controlPosition?: ControlPosition
49
+ /**
50
+ * Radio id attribute
51
+ */
52
+ id?: string
53
+ }
54
+
55
+ const props = withDefaults(defineProps<UiInputRadioProps>(), {
56
+ label: undefined,
57
+ description: undefined,
58
+ labelInfo: undefined,
59
+ icon: undefined,
60
+ id: undefined,
61
+ controlPosition: 'start',
62
+ disabled: false,
63
+ orientation: 'horizontal',
64
+ })
65
+
66
+ const emit = defineEmits<{
67
+ select: [event: Event]
68
+ }>()
69
+
70
+ // Group context: UiInputRadio is only usable inside UiInputRadioGroup
71
+ // (reka's RadioGroupItem enforces this too)
72
+ const group = inject(RADIO_GROUP_KEY, undefined)
73
+ const inChoiceRow = inject(CHOICE_ROW_KEY, false)
74
+ // Controls in the content slot are not the row's control
75
+ provide(CHOICE_ROW_KEY, false)
76
+
77
+ const generatedId = useId()
78
+ const inputId = computed(() => props.id ?? generatedId)
79
+
80
+ // Styling state. Comparison is strict equality: matches reka's isEqual for
81
+ // primitives and null; object values must be reference-stable.
82
+ const isChecked = computed(() => {
83
+ if (!group || group.modelValue.value === undefined) {
84
+ return false
85
+ }
86
+ return group.modelValue.value === props.value
87
+ })
88
+
89
+ const isDisabled = computed(() => props.disabled || (group?.disabled.value ?? false))
90
+ const error = computed(() => group?.error.value ?? false)
91
+ const size = computed(() => group?.size.value ?? 'md')
92
+ const isOptionRow = computed(() => props.orientation === 'horizontal')
93
+ // Side by side, a choice keeps its title on one line: the choice widens or the row
94
+ // wraps instead. A title wider than the whole row truncates.
95
+ const titleOnOneLine = computed(() => inChoiceRow && group?.orientation.value === 'horizontal')
96
+ const controlAtEnd = computed(() => props.orientation === 'horizontal' && props.controlPosition === 'end')
97
+
98
+ const descriptionId = computed(() => props.description ? `${inputId.value}-description` : undefined)
99
+
100
+ const ariaDescribedby = computed(() => {
101
+ const ids = [descriptionId.value, group?.messageId.value].filter(Boolean)
102
+ return ids.length ? ids.join(' ') : undefined
103
+ })
104
+
105
+ const radioClasses = computed(() => {
106
+ const baseClasses = [
107
+ 'relative shrink-0 rounded-full border',
108
+ 'focus:outline-hidden focus-indicator-none',
109
+ 'cursor-pointer disabled:cursor-not-allowed',
110
+ ]
111
+
112
+ const sizeClasses = {
113
+ sm: 'size-unit-0-75rem',
114
+ md: 'size-check-sm',
115
+ lg: 'size-check-md',
116
+ }
117
+
118
+ baseClasses.push(sizeClasses[size.value])
119
+
120
+ // Checked is a thin ring around a dot on the light surface; unchecked is a denser
121
+ // fill than the fields, which once disabled falls back to the field fill with an outline
122
+ if (isChecked.value) {
123
+ baseClasses.push('bg-surface-neutral-bg-main', checkedRingClass.value)
124
+ } else if (isDisabled.value && !error.value) {
125
+ baseClasses.push('bg-control-field-bg border-control-disabled-border')
126
+ } else {
127
+ baseClasses.push('bg-control-check-bg', error.value ? 'border-control-error-border' : 'border-control-field-border')
128
+ if (!isDisabled.value) {
129
+ baseClasses.push('group-hover/option:bg-control-check-bg-hover')
130
+ }
131
+ }
132
+
133
+ return baseClasses
134
+ })
135
+
136
+ // Transform centering (not inset/margins): layout boxes get snapped to
137
+ // device pixels, which renders fractional gaps visibly off-center
138
+ const dotClasses = computed(() => {
139
+ const sizeClasses = {
140
+ sm: 'size-unit-0-375rem',
141
+ md: 'size-unit-0-5rem',
142
+ lg: 'size-unit-0-625rem',
143
+ }
144
+ return ['absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 rounded-full', sizeClasses[size.value]]
145
+ })
146
+
147
+ const checkedRingClass = computed(() => {
148
+ if (error.value) {
149
+ return 'border-actions-danger-idle'
150
+ }
151
+ return isDisabled.value ? 'border-borders-neutral-strong' : 'border-actions-primary-idle'
152
+ })
153
+
154
+ const indicatorClasses = computed(() => {
155
+ if (error.value) {
156
+ return [...dotClasses.value, 'bg-actions-danger-idle']
157
+ }
158
+ return [...dotClasses.value, isDisabled.value ? 'bg-text-neutral-secondary' : 'bg-actions-primary-idle']
159
+ })
160
+
161
+ // A choicebox tints its text with the selection. Disabled, a choicebox recedes
162
+ // whole, while a plain option keeps its selection legible in neutral.
163
+ const tone = computed(() => {
164
+ if (error.value) {
165
+ return 'danger'
166
+ }
167
+ if (isDisabled.value) {
168
+ return isChecked.value && !inChoiceRow ? 'neutral' : 'disabled'
169
+ }
170
+ return isChecked.value && inChoiceRow ? 'selected' : 'neutral'
171
+ })
172
+
173
+ const labelClasses = computed(() => ({
174
+ 'text-content-text-md-medium': inChoiceRow,
175
+ 'text-content-text-md-regular': !inChoiceRow,
176
+ 'text-text-neutral-base': tone.value === 'neutral',
177
+ 'text-text-neutral-tertiary': tone.value === 'disabled',
178
+ 'text-text-danger-base': tone.value === 'danger',
179
+ 'text-text-primary-title': tone.value === 'selected',
180
+ 'flex items-center min-w-0': titleOnOneLine.value,
181
+ 'cursor-not-allowed': isDisabled.value,
182
+ 'cursor-pointer': !isDisabled.value,
183
+ }))
184
+
185
+ const descriptionClasses = computed(() => ({
186
+ 'text-center': props.orientation === 'vertical',
187
+ 'text-text-neutral-secondary': tone.value === 'neutral',
188
+ 'text-text-neutral-tertiary': tone.value === 'disabled',
189
+ 'text-text-danger-tertiary': tone.value === 'danger',
190
+ 'text-text-primary-base': tone.value === 'selected',
191
+ 'cursor-not-allowed': isDisabled.value,
192
+ 'cursor-pointer': !isDisabled.value,
193
+ }))
194
+
195
+ const innerWrapperClasses = computed(() => {
196
+ const classes: string[] = ['group/option relative flex h-full']
197
+
198
+ classes.push(props.orientation === 'vertical' ? 'flex-col items-center' : 'items-start')
199
+
200
+ if (isOptionRow.value && !inChoiceRow) {
201
+ // The row bleeds around the option without taking layout space, so a one-line
202
+ // option is a 32px hit target; hover shows on the glyph only. Inside UiChoice the choice draws the row.
203
+ classes.push('isolate before:absolute before:-inset-x-3 before:-inset-y-1.5 before:-z-10 before:rounded-field-md has-[[role=radio]:focus-visible]:before:focus-ring')
204
+ } else if (props.orientation === 'vertical') {
205
+ classes.push(inChoiceRow ? 'rounded-field-md' : 'rounded-field-md has-[[role=radio]:focus-visible]:focus-ring')
206
+ }
207
+
208
+ classes.push(isDisabled.value ? 'cursor-not-allowed' : 'cursor-pointer')
209
+
210
+ return classes
211
+ })
212
+
213
+ const glyphProps = computed(() => ({
214
+ inputId: inputId.value,
215
+ value: props.value,
216
+ disabled: props.disabled,
217
+ error: error.value,
218
+ ariaDescribedby: ariaDescribedby.value,
219
+ radioClasses: radioClasses.value,
220
+ indicatorClasses: indicatorClasses.value,
221
+ }))
222
+
223
+ const glyphRef = ref<InstanceType<typeof RadioGlyph> | null>(null)
224
+
225
+ function radioElement (): HTMLElement | undefined {
226
+ return glyphRef.value?.el()
227
+ }
228
+
229
+ // Make the whole wrapper (description, padding) select the radio.
230
+ // Clicks on the button itself or on the label are skipped: the button handles
231
+ // its own clicks and the label forwards to the button natively via `for`.
232
+ function onWrapperClick (event: MouseEvent) {
233
+ if (isDisabled.value) {
234
+ return
235
+ }
236
+
237
+ const target = event.target as HTMLElement
238
+ const radioEl = radioElement()
239
+
240
+ if (!radioEl || target === radioEl || radioEl.contains(target)) {
241
+ return
242
+ }
243
+ if (target.closest('label')) {
244
+ return
245
+ }
246
+ // Controls in the content slot handle their own clicks
247
+ if (target.closest('input, textarea, select, button, a')) {
248
+ return
249
+ }
250
+
251
+ radioEl.click()
252
+ }
253
+
254
+ defineExpose({
255
+ focus: () => radioElement()?.focus(),
256
+ blur: () => radioElement()?.blur(),
257
+ })
258
+ </script>
259
+
260
+ <template>
261
+ <!-- An option that hosts a field or pushes its radio to the end fills its
262
+ slot; otherwise it shrink-wraps to keep the click zone on content -->
263
+ <div
264
+ class="ui-input-radio-wrapper"
265
+ data-testid="ui-input-radio"
266
+ :class="[$slots.content || controlAtEnd ? 'w-full' : 'w-fit', { 'max-w-full': titleOnOneLine }]"
267
+ >
268
+ <div
269
+ :class="innerWrapperClasses"
270
+ data-testid="ui-input-radio-row"
271
+ @click="onWrapperClick"
272
+ >
273
+ <!-- Radio - horizontal only. The box matches the label's line-height so
274
+ the glyph centers on the label line, and the checkbox's width so
275
+ labels line up; the glyph hugs the outer edge like a checkbox does -->
276
+ <span
277
+ v-if="orientation === 'horizontal'"
278
+ class="flex size-5 items-center shrink-0"
279
+ :class="controlAtEnd ? ['order-last justify-end', inChoiceRow ? 'ms-5' : 'ms-3'] : ['justify-start', inChoiceRow ? 'me-3' : 'me-1']"
280
+ >
281
+ <RadioGlyph
282
+ ref="glyphRef"
283
+ v-bind="glyphProps"
284
+ @select="emit('select', $event)"
285
+ />
286
+ </span>
287
+
288
+ <!-- Optional icon: between the radio and the text, or above the text in
289
+ vertical orientation. min-h-5 keeps a small icon on the label line -->
290
+ <span
291
+ v-if="icon || $slots.icon"
292
+ class="flex items-center justify-center shrink-0"
293
+ data-testid="ui-input-radio-icon"
294
+ :class="orientation === 'vertical' ? 'mb-2' : 'mr-2 min-h-5'"
295
+ >
296
+ <slot name="icon">
297
+ <span
298
+ class="size-4"
299
+ :class="[icon, {
300
+ 'text-text-neutral-base': tone === 'neutral',
301
+ 'text-text-neutral-tertiary': tone === 'disabled',
302
+ 'text-text-danger-base': tone === 'danger',
303
+ 'text-text-primary-title': tone === 'selected',
304
+ }]"
305
+ />
306
+ </slot>
307
+ </span>
308
+
309
+ <div
310
+ class="flex flex-col min-w-0"
311
+ :class="[orientation === 'vertical' ? 'items-center' : 'flex-1', inChoiceRow ? 'gap-1' : 'gap-0.5']"
312
+ data-testid="ui-input-radio-text"
313
+ >
314
+ <Label
315
+ :for="inputId"
316
+ :class="labelClasses"
317
+ >
318
+ <span
319
+ v-if="titleOnOneLine"
320
+ class="truncate"
321
+ >
322
+ <slot name="label">
323
+ {{ label }}
324
+ </slot>
325
+ </span>
326
+ <slot
327
+ v-else
328
+ name="label"
329
+ >
330
+ {{ label }}
331
+ </slot>
332
+
333
+ <!-- icon: inline so it wraps with the text instead of centering against the whole label block,
334
+ and the word joiner keeps it on the line of the last word.
335
+ -translate-y-px because the icon aligns with the text so that its visually centered -->
336
+ <span
337
+ v-if="labelInfo"
338
+ class="whitespace-nowrap before:content-['\2060']"
339
+ ><UiTooltip :content="labelInfo">
340
+ <span
341
+ class="inline-flex items-center justify-center size-4 ms-2 align-middle -translate-y-px cursor-help shrink-0"
342
+ :class="{
343
+ 'text-text-neutral-tertiary': tone === 'neutral' || tone === 'disabled',
344
+ 'text-text-danger-tertiary': tone === 'danger',
345
+ 'text-text-primary-tertiary': tone === 'selected',
346
+ }"
347
+ data-testid="ui-input-radio-label-info"
348
+ @click.stop.prevent
349
+ >
350
+ <span class="i-ph-info-fill size-4" />
351
+ </span>
352
+ </UiTooltip></span>
353
+ </Label>
354
+
355
+ <span
356
+ v-if="description"
357
+ :id="descriptionId"
358
+ class="text-content-text-md-regular"
359
+ :class="descriptionClasses"
360
+ >
361
+ {{ description }}
362
+ </span>
363
+
364
+ <!-- Extra content under the text, e.g. a field the option configures.
365
+ Sits outside the label so it stays out of the accessible name -->
366
+ <slot name="content" />
367
+ </div>
368
+
369
+ <!-- Radio - vertical only -->
370
+ <span
371
+ v-if="orientation === 'vertical'"
372
+ class="flex justify-center mt-2"
373
+ >
374
+ <RadioGlyph
375
+ ref="glyphRef"
376
+ v-bind="glyphProps"
377
+ @select="emit('select', $event)"
378
+ />
379
+ </span>
380
+ </div>
381
+ </div>
382
+ </template>
@@ -0,0 +1,166 @@
1
+ <script setup lang="ts">
2
+ import { computed, provide, ref, toRef, watch } from 'vue'
3
+ import { RadioGroupRoot, useId } from 'reka-ui'
4
+ import type { RadioSize, RadioValue } from './types'
5
+ import { RADIO_GROUP_KEY } from './types'
6
+
7
+ interface UiInputRadioGroupProps {
8
+ /**
9
+ * The selected value. `undefined` means no selection;
10
+ * `null` is a valid option value.
11
+ */
12
+ modelValue?: RadioValue
13
+ /**
14
+ * Name attribute, submitted with the form via a hidden input
15
+ */
16
+ name?: string
17
+ /**
18
+ * Whether the whole group is disabled
19
+ * @default false
20
+ */
21
+ disabled?: boolean
22
+ /**
23
+ * Whether arrow-key navigation loops from last item to first
24
+ * @default true
25
+ */
26
+ loop?: boolean
27
+ /**
28
+ * Whether a selection is required
29
+ * @default false
30
+ */
31
+ required?: boolean
32
+ /**
33
+ * Whether the group is in an error state
34
+ * @default false
35
+ */
36
+ error?: boolean
37
+ /**
38
+ * Error message displayed below the group
39
+ */
40
+ errorMessage?: string
41
+ /**
42
+ * Size of the radio items
43
+ * @default 'md'
44
+ */
45
+ size?: RadioSize
46
+ /**
47
+ * Layout direction of the items. Also sets the group's `aria-orientation`
48
+ * and the arrow-key navigation axis to match.
49
+ * @default 'vertical'
50
+ */
51
+ orientation?: 'vertical' | 'horizontal'
52
+ /**
53
+ * Whether the group stretches to fill its container instead of
54
+ * shrink-wrapping its items
55
+ * @default false
56
+ */
57
+ fullWidth?: boolean
58
+ /**
59
+ * Id of the element naming the group, set on the radiogroup itself
60
+ */
61
+ ariaLabelledby?: string
62
+ }
63
+
64
+ const props = withDefaults(defineProps<UiInputRadioGroupProps>(), {
65
+ modelValue: undefined,
66
+ name: undefined,
67
+ errorMessage: undefined,
68
+ disabled: false,
69
+ loop: true,
70
+ required: false,
71
+ error: false,
72
+ size: 'md',
73
+ orientation: 'vertical',
74
+ fullWidth: false,
75
+ ariaLabelledby: undefined,
76
+ })
77
+
78
+ const emit = defineEmits<{
79
+ 'update:modelValue': [value: RadioValue]
80
+ }>()
81
+
82
+ // Track the current value locally so items can style themselves even when
83
+ // the consumer does not bind v-model (uncontrolled usage)
84
+ const currentValue = ref<RadioValue | undefined>(props.modelValue)
85
+
86
+ watch(() => props.modelValue, (value) => {
87
+ currentValue.value = value
88
+ })
89
+
90
+ function onUpdateModelValue (value: RadioValue) {
91
+ currentValue.value = value
92
+ emit('update:modelValue', value)
93
+ }
94
+
95
+ const groupId = useId()
96
+
97
+ const displayMessage = computed(() => {
98
+ if (props.error && props.errorMessage) {
99
+ return props.errorMessage
100
+ }
101
+ return undefined
102
+ })
103
+
104
+ const messageId = computed(() => displayMessage.value ? `${groupId}-message` : undefined)
105
+
106
+ // items-stretch keeps card tiles equal-height when they share a row. The 12px
107
+ // gap holds the 6px bleed of each option row, so one-line rows stay 32px hit
108
+ // targets that never overlap.
109
+ const rootClasses = computed(() =>
110
+ props.orientation === 'horizontal'
111
+ ? 'flex flex-row flex-wrap items-stretch gap-3'
112
+ : 'flex flex-col gap-3',
113
+ )
114
+
115
+ // mt-1.5 clears the 6px bleed of the last option row
116
+ const displayMessageClasses = computed(() => {
117
+ const baseClasses = ['text-content-legends-bold-md mt-1.5']
118
+
119
+ if (props.error) {
120
+ baseClasses.push('text-text-danger-body')
121
+ } else {
122
+ baseClasses.push('text-text-neutral-secondary')
123
+ }
124
+
125
+ return baseClasses
126
+ })
127
+
128
+ provide(RADIO_GROUP_KEY, {
129
+ modelValue: currentValue,
130
+ error: toRef(props, 'error'),
131
+ size: toRef(props, 'size'),
132
+ orientation: toRef(props, 'orientation'),
133
+ disabled: toRef(props, 'disabled'),
134
+ messageId,
135
+ })
136
+ </script>
137
+
138
+ <template>
139
+ <div
140
+ class="ui-input-radio-group-wrapper"
141
+ :class="fullWidth ? 'w-full' : 'w-fit'"
142
+ >
143
+ <RadioGroupRoot
144
+ :model-value="currentValue"
145
+ :name="name"
146
+ :disabled="disabled"
147
+ :loop="loop"
148
+ :required="required"
149
+ :orientation="orientation"
150
+ :aria-labelledby="ariaLabelledby"
151
+ :class="rootClasses"
152
+ @update:model-value="onUpdateModelValue"
153
+ >
154
+ <slot />
155
+ </RadioGroupRoot>
156
+
157
+ <!-- Error Message -->
158
+ <p
159
+ v-if="displayMessage"
160
+ :id="messageId"
161
+ :class="displayMessageClasses"
162
+ >
163
+ {{ displayMessage }}
164
+ </p>
165
+ </div>
166
+ </template>
@@ -0,0 +1,24 @@
1
+ import type { InjectionKey, Ref } from 'vue'
2
+ import type { AcceptableValue } from 'reka-ui'
3
+
4
+ export type RadioSize = 'sm' | 'md' | 'lg'
5
+
6
+ /**
7
+ * Valid radio option values. `null` is a supported option value;
8
+ * `undefined` means "no selection". Object values must be reference-stable.
9
+ */
10
+ export type RadioValue = AcceptableValue
11
+
12
+ export interface RadioGroupContext {
13
+ /** Current group value (kept in sync in both controlled and uncontrolled mode) */
14
+ modelValue: Readonly<Ref<RadioValue | undefined>>
15
+ error: Readonly<Ref<boolean>>
16
+ size: Readonly<Ref<RadioSize>>
17
+ orientation: Readonly<Ref<'vertical' | 'horizontal'>>
18
+ disabled: Readonly<Ref<boolean>>
19
+ /** id of the group error message, for aria-describedby on items */
20
+ messageId: Readonly<Ref<string | undefined>>
21
+ }
22
+
23
+ export const RADIO_GROUP_KEY: InjectionKey<RadioGroupContext> =
24
+ Symbol('ui-input-radio-group')
@@ -7,6 +7,7 @@ import UiDropdownContent from '../UiDropdown/UiDropdownContent.vue'
7
7
  import UiDropdownItem from '../UiDropdown/UiDropdownItem.vue'
8
8
  import type { AcceptableValue } from 'reka-ui'
9
9
  import type { SelectSize } from './types'
10
+ import { useControlShape } from '../../composables/useControlShape'
10
11
 
11
12
  interface UiInputSelectProps {
12
13
  size?: SelectSize
@@ -57,6 +58,8 @@ const props = withDefaults(defineProps<UiInputSelectProps>(), {
57
58
  multiple: false,
58
59
  })
59
60
 
61
+ const shape = useControlShape('field', () => props.size)
62
+
60
63
  // Sentinel value for the "clear" option — intentionally verbose to avoid collisions with real values
61
64
  const CLEAR_VALUE = '__ui-input-select-clear__'
62
65
 
@@ -110,13 +113,12 @@ function handleUpdate (value: AcceptableValue) {
110
113
  <!-- Loading State -->
111
114
  <div
112
115
  v-if="loading"
113
- class="flex items-center gap-2 w-full rounded-lg border border-solid border-borders-neutral-strong bg-actions-neutral-idle-alpha-light"
114
- :class="{
115
- 'h-8 px-2': size === 'sm',
116
- 'h-10 px-3': size === 'md',
117
- 'h-12 px-4': size === 'lg',
118
- 'h-16 px-4': size === 'xl',
119
- }"
116
+ class="flex items-center gap-2 w-full border border-solid border-control-field-border bg-control-field-bg"
117
+ :class="[shape.radius, shape.height, {
118
+ 'px-2': size === 'sm',
119
+ 'px-3': size === 'md',
120
+ 'px-4': size === 'lg' || size === 'xl',
121
+ }]"
120
122
  >
121
123
  <div class="h-3 w-24 bg-surface-neutral-alpha-400 rounded-md animate-pulse" />
122
124
  </div>