@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,8 +1,8 @@
1
1
  <script setup lang="ts">
2
- import { computed, onBeforeUnmount, onMounted, ref, watch, getCurrentInstance, type Slot } from 'vue'
2
+ import { computed, onBeforeUnmount, ref, watch, getCurrentInstance, type Slot } from 'vue'
3
3
  import { BREAKPOINT_LG } from '../../utils/breakpoints'
4
4
  import { useResponsive } from '../../utils/responsive'
5
- import useThemeAndModes from '../../composables/useThemeAndModes'
5
+ import { useContentMode } from '../../composables/useContentMode'
6
6
  import { useMobileMenu } from '../../composables/useMobileMenu'
7
7
  import UiLoader from '../UiLoader/UiLoader.vue'
8
8
 
@@ -36,35 +36,176 @@ import UiLoader from '../UiLoader/UiLoader.vue'
36
36
  // │ │ └─────────────────────────────────────────────────┘ │
37
37
  // └─┴─────────────────────────────────────────────────────┘
38
38
 
39
+ /**
40
+ * Headroom sizes, in px: the gap above the content card that lets the shell
41
+ * background show through. One map so the page frame has a single source of
42
+ * truth — retune here rather than at call sites.
43
+ */
44
+ const HEADROOM_GAP_PX = {
45
+ none: 0,
46
+ sm: 4,
47
+ md: 10,
48
+ lg: 16,
49
+ xl: 24,
50
+ } as const
51
+
52
+ type HeadroomGap = keyof typeof HEADROOM_GAP_PX
53
+
54
+ /** Used when the card sits flush at the top and no size is given. */
55
+ const DEFAULT_HEADROOM_GAP: HeadroomGap = 'md'
56
+
39
57
  const props = withDefaults(defineProps<{
40
- /**
41
- * Extra classes for the inner content `<section>`. Use to override the
42
- * default surface, e.g. `bg-surface-neutral-300` instead of the standard
43
- * `bg-surface-neutral-bg-main`. Appended after the defaults so it wins on
44
- * conflicting utilities.
45
- */
46
- pageContentClasses?: string
47
58
  /**
48
59
  * Page title surfaced as the label of UiShell's fallback mobile menu topbar
49
60
  * (shown on a headerless page that has a side menu). Lets a page name its
50
61
  * menu row — e.g. "Home" — without reaching into useMobileMenu directly.
51
62
  */
52
63
  title?: string
64
+ /**
65
+ * The shape of the page inside the content card. Padded shapes use the
66
+ * page padding P (16px below md, 24px from md), which the page exposes to
67
+ * its content as `var(--ui-page-padding)`. P applies inside the card only:
68
+ * the page header and the tabs above it keep their own spacing.
69
+ * - `document` (default): the card scrolls as a single column and pads
70
+ * its content with P on every side. Forms, settings, content blocks.
71
+ * - `list`: like `document`, but the content is padded with P at the top
72
+ * only: toolbars, tables and pagination run from card edge to card edge
73
+ * and pad their sides with `--ui-page-inline`. The content is held to the
74
+ * card height, so a child that may shrink (`flex-1 min-h-0`) becomes the
75
+ * scroll region and a table header in it stays in view; anything taller
76
+ * still scrolls the card.
77
+ * - `app`: the card fills the available height and never scrolls itself;
78
+ * the page owns its scroll regions and layout, so the content is not
79
+ * padded. Each region pads itself with `var(--ui-page-padding)`.
80
+ *
81
+ * Edge-to-edge bands (toolbars, pagination) read `--ui-page-inline` for
82
+ * their side padding. UiDataTable pads its scroll region with it, so a
83
+ * wide table scrolls from card edge to card edge. It is set to P on a
84
+ * `list` page only and left unset everywhere else, so a band
85
+ * picks its inset with one class:
86
+ * - `px-(--ui-page-inline)`: P on a list page, 0 elsewhere.
87
+ * - `px-(--ui-page-inline,var(--spacing-space-4))`: P on a list page, the
88
+ * band's own inset elsewhere.
89
+ *
90
+ * A page with a `leftSlot` keeps the split layout insets instead of P: its
91
+ * header slot aligns with the aside, not with the content.
92
+ */
93
+ variant?: 'document' | 'list' | 'app'
94
+ /**
95
+ * Column split when a leftSlot is present: `third` (default) gives the
96
+ * aside 1/3 and the card 2/3; `half` splits them evenly; `fill` lets the
97
+ * aside take all remaining width while the card hugs its content — for
98
+ * app pages whose card is a fixed-width side panel (e.g. the room's
99
+ * interactions sidepane).
100
+ */
101
+ leftSlotRatio?: 'third' | 'half' | 'fill'
102
+ /**
103
+ * How the content card sits in the frame.
104
+ * - `anchored` (default): the dashboard page card — flush to the bottom and
105
+ * right edges, rounded top-left, page-card chrome (level-3 elevation).
106
+ * - `floating`: a detached card with a thin gutter on every side, rounded
107
+ * all around, elevated-card chrome (level-2 elevation + inner glow +
108
+ * container border). The room's panel treatment.
109
+ */
110
+ frame?: 'anchored' | 'floating'
111
+ /**
112
+ * Viewport gate for the leftSlot: `lg` (default) renders the aside only
113
+ * from the lg breakpoint — the dashboard behavior, where the aside is
114
+ * secondary. `none` keeps the aside at every width, for app pages whose
115
+ * aside is primary content (the room's stage, the lobby's session
116
+ * information) and must never unmount with the viewport.
117
+ */
118
+ leftSlotBreakpoint?: 'lg' | 'none'
119
+ /**
120
+ * Headroom above the content card, letting the shell background show
121
+ * through. Sizes map to px via `HEADROOM_GAP_PX`.
122
+ *
123
+ * Left unset, the card gets `DEFAULT_HEADROOM_GAP` only when it sits flush
124
+ * at the top — i.e. there is no page header or left slot already offsetting
125
+ * it. Naming a size applies it in either case, and `none` removes the gap
126
+ * on a page that would otherwise get the default.
127
+ */
128
+ headroomGap?: HeadroomGap
129
+ /**
130
+ * Drops the content padding a `document` or `list` page gets by default, so the
131
+ * page's content reaches the card edges. Only for pages whose layout is
132
+ * edge-to-edge by design: full-bleed bands, or scroll regions that must
133
+ * sit against the card edge. Has no effect on `app` pages, which never get
134
+ * the padding.
135
+ */
136
+ flush?: boolean
137
+ /**
138
+ * The fill of an anchored content card.
139
+ * - `container` (default): the white content surface.
140
+ * - `background`: the grey page background, for screens whose content is
141
+ * already cards or regions, so they do not sit in a second white box.
142
+ * A floating card keeps its elevated fill.
143
+ */
144
+ surface?: 'container' | 'background'
53
145
  }>(), {
54
- pageContentClasses: '',
55
146
  title: undefined,
147
+ variant: 'document',
148
+ leftSlotRatio: 'third',
149
+ frame: 'anchored',
150
+ leftSlotBreakpoint: 'lg',
151
+ headroomGap: undefined,
152
+ flush: false,
153
+ surface: 'container',
56
154
  })
57
155
 
58
156
  const slots = defineSlots<{
59
157
  banner?(): Slot
60
158
  pageHeader?(): Slot
61
159
  pageTabs?(): Slot
160
+ /**
161
+ * Summary on the page background, between the page header (and tabs) and
162
+ * the content card, e.g. stat cards. It shares the page header's edges.
163
+ */
164
+ pageSummary?(): Slot
62
165
  leftSlot?(): Slot
63
166
  header?(): Slot
64
167
  footer?(): Slot
65
168
  default?(): Slot
66
169
  }>()
67
170
 
171
+ // An `app` page manages its own scrolling: the card clips and its row is
172
+ // pinned to the viewport, otherwise a split-pane editor gets a second, outer scrollbar.
173
+ const isApp = computed(() => props.variant === 'app')
174
+ const scrollClass = computed(() => (isApp.value ? 'overflow-hidden' : 'overflow-y-auto'))
175
+ const fillsCard = computed(() => isApp.value || props.variant === 'list')
176
+
177
+ // Spelled out as literals: Tailwind only generates classes it can see as
178
+ // complete strings in the source.
179
+ const pageColumnsClass = computed(() => {
180
+ const gated = props.leftSlotBreakpoint === 'lg'
181
+ if (props.leftSlotRatio === 'half') {
182
+ return gated ? 'has-[>:nth-child(2)]:lg:grid-cols-[1fr_1fr]' : 'has-[>:nth-child(2)]:grid-cols-[1fr_1fr]'
183
+ }
184
+ if (props.leftSlotRatio === 'fill') {
185
+ return gated ? 'has-[>:nth-child(2)]:lg:grid-cols-[minmax(0,1fr)_auto]' : 'has-[>:nth-child(2)]:grid-cols-[minmax(0,1fr)_auto]'
186
+ }
187
+ return gated ? 'has-[>:nth-child(2)]:lg:grid-cols-[1fr_2fr]' : 'has-[>:nth-child(2)]:grid-cols-[1fr_2fr]'
188
+ })
189
+
190
+ // Frame treatment. The chrome pairs are exclusive per frame (never merged),
191
+ // so conflicting utilities can't race on stylesheet order.
192
+ const isFloating = computed(() => props.frame === 'floating')
193
+ const frameGutterClass = computed(() => (isFloating.value ? 'p-1 gap-x-1' : 'lg:gap-x-8'))
194
+ const cardRadiusClass = computed(() => (isFloating.value ? 'rounded-container' : 'rounded-tl-container'))
195
+ // Anchored keeps the page-card chrome: shadow-elevation-level-3 keeps
196
+ // drop-shadow-2xl's exact geometry and light-mode colors, but its colors
197
+ // resolve to transparent in dark content mode — the card no longer draws a
198
+ // dark ring on the dark shell. Floating takes the elevated-card treatment
199
+ // from the elevation proposal (level 2 + inner glow + container border).
200
+ const anchoredFillClass = computed(() => (
201
+ props.surface === 'background' ? 'bg-surface-neutral-300' : 'bg-surface-neutral-bg-main'
202
+ ))
203
+ const cardChromeClass = computed(() => (
204
+ isFloating.value
205
+ ? 'bg-elevation-2 shadow-elevation-level-2 inset-shadow-inner-glow border border-container-border'
206
+ : `${anchoredFillClass.value} shadow-elevation-level-3`
207
+ ))
208
+
68
209
  // Check for slot presence
69
210
  const bannerSlot = computed(() => !!slots.banner)
70
211
  const pageHeaderSlot = computed(() => !!slots.pageHeader)
@@ -73,6 +214,80 @@ const leftSlot = computed(() => !!slots.leftSlot)
73
214
  const headerSlot = computed(() => !!slots.header)
74
215
  const footerSlot = computed(() => !!slots.footer)
75
216
 
217
+ const PAGE_INSETS = {
218
+ header: 'px-(--ui-page-padding) pt-(--ui-page-padding)',
219
+ headerBottom: 'pb-(--ui-page-padding)',
220
+ content: 'p-(--ui-page-padding)',
221
+ }
222
+ // A split page aligns its header with the aside rather than with the content.
223
+ const SPLIT_INSETS = {
224
+ header: 'px-4 pt-4 lg:px-10 lg:pt-8',
225
+ headerBottom: 'pb-4 lg:pb-8',
226
+ content: 'px-4 py-4 lg:px-10 lg:pt-8 lg:pb-10',
227
+ }
228
+ const isSplitLayout = computed(() => leftSlot.value)
229
+ // The header slot and the content share one side inset so their left edges align.
230
+ const insets = computed(() => (isSplitLayout.value ? SPLIT_INSETS : PAGE_INSETS))
231
+
232
+ const pageInlineClass = computed(() => (props.variant === 'list' ? '[--ui-page-inline:var(--ui-page-padding)]' : ''))
233
+
234
+ const isContentPadded = computed(() => !isApp.value && !props.flush)
235
+ const contentPaddingClass = computed(() => {
236
+ if (!isContentPadded.value) return ''
237
+ return props.variant === 'list' ? 'pt-(--ui-page-padding)' : insets.value.content
238
+ })
239
+ // The padded content's top padding already spaces it from the header.
240
+ const headerBottomPaddingClass = computed(() => (isContentPadded.value ? '' : insets.value.headerBottom))
241
+
242
+ // Who scrolls: the page-content row, or the card inside it?
243
+ // - `app`: neither — the page owns its scroll regions. The row is pinned to the
244
+ // viewport but left unclipped, or it would eat the card's elevation shadow.
245
+ // - leftSlot layout: the row. The aside can be taller than the viewport and
246
+ // scrolls together with the card (historical auth-page behavior). The 1px
247
+ // top padding keeps the card's elevation ring inside the scrollport.
248
+ // - plain `document`: the card. Its row is pinned to the viewport height
249
+ // (minmax(0,1fr)) so the card's own overflow-y-auto engages, and the row
250
+ // stays unclipped — a scrollport would clip the card's elevation shadow
251
+ // at the flush edges.
252
+ const pageContentScrollClass = computed(() => {
253
+ if (isApp.value) return 'overflow-visible grid-rows-[minmax(0,1fr)]'
254
+ return leftSlot.value
255
+ ? 'overflow-y-auto pt-px'
256
+ : 'overflow-visible grid-rows-[minmax(0,1fr)]'
257
+ })
258
+
259
+ // Row track for the card grid. An `app` or `list` page needs its main row to take the
260
+ // leftover height and be allowed to shrink (minmax(0,1fr)), so nested
261
+ // overflow containers resolve against a real height instead of growing.
262
+ // Spelled out as literals: Tailwind only generates classes it can see as
263
+ // complete strings in the source.
264
+ const cardRowsClass = computed(() => {
265
+ if (!fillsCard.value) { return headerSlot.value ? 'grid-rows-[min-content]' : '' }
266
+ if (headerSlot.value && footerSlot.value) { return 'grid-rows-[min-content_minmax(0,1fr)_min-content]' }
267
+ if (headerSlot.value) { return 'grid-rows-[min-content_minmax(0,1fr)]' }
268
+ if (footerSlot.value) { return 'grid-rows-[minmax(0,1fr)_min-content]' }
269
+ return 'grid-rows-[minmax(0,1fr)]'
270
+ })
271
+
272
+ // A named size always wins; otherwise fall back to the default, and only when
273
+ // the card is flush at the top (a page header or left slot already provides
274
+ // the offset). Applied as an inline style rather than a utility so the value
275
+ // does not depend on Tailwind's `--spacing` fallback resolving to 0.25rem.
276
+ // A zero gap emits no style at all, so `none` and "already offset" produce
277
+ // identical markup.
278
+ //
279
+ // Padding on the clipping wrapper rather than a margin on the card: the card
280
+ // is `h-full`, so a margin would push a full-height box down inside a fixed
281
+ // height, overflowing by exactly the gap and clipping that much off the
282
+ // bottom. As padding it shrinks the box the card measures itself against.
283
+ const headroomGapStyle = computed(() => {
284
+ const size = props.headroomGap ?? (
285
+ !pageHeaderSlot.value && !leftSlot.value ? DEFAULT_HEADROOM_GAP : 'none'
286
+ )
287
+ const px = HEADROOM_GAP_PX[size]
288
+ return px ? { paddingTop: `${px}px` } : undefined
289
+ })
290
+
76
291
  // Feed the page title to UiShell's fallback mobile menu topbar. Watched so a
77
292
  // reactive title updates live, and cleared on unmount so it doesn't leak to
78
293
  // the next route.
@@ -87,33 +302,16 @@ if (!app) {
87
302
  // Prefer the app-installed $responsive (it may carry custom flags); fall
88
303
  // back to the shared singleton so no plugin registration is required.
89
304
  const responsive = computed(() => app.appContext.config.globalProperties.$responsive ?? useResponsive())
90
- const displayLeftSlot = computed(() => responsive.value.width >= BREAKPOINT_LG)
305
+ const displayLeftSlot = computed(() => (
306
+ props.leftSlotBreakpoint === 'none' || responsive.value.width >= BREAKPOINT_LG
307
+ ))
91
308
 
92
309
  const UiPage = ref<HTMLElement | undefined>()
93
310
  const PageContent = ref<HTMLElement | undefined>()
94
311
 
95
- const { addElementToWatch } = useThemeAndModes()
96
-
97
- // The page content historically forces light mode. Only lift that pin when
98
- // the internal theming tooling is enabled, so the stored content mode
99
- // (driven by the ThemePicker) can apply; for everyone else the content
100
- // stays light regardless of what localStorage may hold.
101
- // Read in onMounted, not at setup: setup also runs during SSR (no
102
- // localStorage), and server + client-hydration markup must be identical —
103
- // the pin lifts right after mount for the internal tooling instead.
104
- const isInternalThemingEnabled = ref(false)
105
-
106
- onMounted(() => {
107
- isInternalThemingEnabled.value = localStorage.getItem('ls-internal-theming') === 'true'
108
-
109
- addElementToWatch(
110
- 'content',
111
- {
112
- selector: '[data-name="ui-page-content"]',
113
- element: PageContent.value!,
114
- ...(isInternalThemingEnabled.value ? {} : { modeOverride: 'light' }),
115
- })
116
- })
312
+ // Follow the stored content mode via the shared composable (also used by the
313
+ // settings shell), so the page content's data-colors-semantic stays in sync.
314
+ const { contentMode } = useContentMode(PageContent, '[data-name="ui-page-content"]')
117
315
 
118
316
  </script>
119
317
 
@@ -121,7 +319,10 @@ onMounted(() => {
121
319
  <main
122
320
  ref="UiPage"
123
321
  data-name="ui-page-wrapper"
124
- class="ui-page h-full flex flex-col"
322
+ :class="[
323
+ 'ui-page h-full flex flex-col [--ui-page-padding:var(--spacing-space-4)] md:[--ui-page-padding:var(--spacing-space-6)]',
324
+ pageInlineClass,
325
+ ]"
125
326
  >
126
327
  <section
127
328
  v-if="bannerSlot"
@@ -137,7 +338,7 @@ onMounted(() => {
137
338
  v-if="pageHeaderSlot"
138
339
  ref="pageHeader"
139
340
  data-name="pageHeader"
140
- class="page-header p-4 md:p-8 md:pl-0 relative"
341
+ class="page-header p-4 md:px-8 md:py-6 md:pl-0 relative"
141
342
  >
142
343
  <slot name="pageHeader" />
143
344
  </section>
@@ -153,10 +354,21 @@ onMounted(() => {
153
354
  </section>
154
355
 
155
356
  <section
156
- ref="PageContent"
357
+ v-if="$slots.pageSummary"
358
+ data-name="pageSummary"
359
+ class="px-4 pb-(--ui-page-padding) md:pl-0 md:pr-8"
360
+ >
361
+ <slot name="pageSummary" />
362
+ </section>
363
+
364
+ <section
157
365
  data-name="pageContent"
158
- class="page-content flex-1 overflow-y-auto w-full grid has-[>:nth-child(2)]:lg:grid-cols-[1fr_2fr] lg:gap-x-8 min-h-0"
159
- :data-colors-semantic="isInternalThemingEnabled ? undefined : 'light'"
366
+ :class="[
367
+ 'page-content flex-1 w-full grid min-h-0',
368
+ frameGutterClass,
369
+ pageColumnsClass,
370
+ pageContentScrollClass,
371
+ ]"
160
372
  >
161
373
  <aside
162
374
  v-if="leftSlot && displayLeftSlot"
@@ -166,29 +378,43 @@ onMounted(() => {
166
378
  <slot name="leftSlot" />
167
379
  </aside>
168
380
 
169
- <div class="min-w-0 overflow-hidden rounded-tl-xl">
381
+ <!-- No overflow-hidden here: the card handles its own content
382
+ clipping via scrollClass, and clipping this wrapper would eat
383
+ the card's elevation shadow on every side but the top. -->
384
+ <div
385
+ ref="PageContent"
386
+ data-name="ui-page-content"
387
+ :class="['min-w-0', cardRadiusClass]"
388
+ :style="headroomGapStyle"
389
+ :data-colors-semantic="contentMode"
390
+ >
170
391
  <section
171
392
  :class="[
172
- 'content min-w-0 overflow-y-auto h-full bg-surface-neutral-bg-main grid shadow-shadows-drop-shadow-2xl rounded-tl-xl',
173
- // Nudge the content down only when it sits flush at the top — i.e.
174
- // no page header and no left slot to align against.
175
- !pageHeaderSlot && !leftSlot ? 'mt-1' : '',
176
- headerSlot ? 'grid-rows-[min-content]' : '',
177
- props.pageContentClasses,
393
+ 'content min-w-0 h-full grid [view-transition-name:ui-page-card]',
394
+ cardChromeClass,
395
+ cardRadiusClass,
396
+ scrollClass,
397
+ cardRowsClass,
178
398
  ]"
179
399
  data-name="ui-shell-content"
180
400
  >
181
401
  <section
182
402
  v-if="headerSlot"
183
403
  data-name="header"
184
- class="content-header p-4 sm:p-6"
404
+ :class="['content-header', insets.header, headerBottomPaddingClass]"
185
405
  >
186
406
  <slot name="header" />
187
407
  </section>
188
408
 
189
409
  <section
190
410
  data-name="main-content"
191
- class="route-content w-full min-w-0 flex flex-col"
411
+ :class="[
412
+ 'route-content w-full min-w-0 flex flex-col',
413
+ contentPaddingClass,
414
+ // Let the row shrink so an editor's own overflow containers
415
+ // resolve against a real height. Document pages keep growing.
416
+ fillsCard ? 'min-h-0' : '',
417
+ ]"
192
418
  >
193
419
  <slot>
194
420
  <UiLoader />
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, onBeforeUnmount, useSlots, watch, type Slot } from 'vue'
3
- import LivestormLogo from '@livestorm/ui-tokens/output/vectors/logos/livestorm-white.svg'
4
3
  import UiButton from '../UiButton/UiButton.vue'
4
+ import UiLogo from '../UiLogo/UiLogo.vue'
5
5
  import { useMobileMenu } from '../../composables/useMobileMenu'
6
6
  import type { RouteLocationRaw } from 'vue-router'
7
7
 
@@ -65,8 +65,15 @@ onBeforeUnmount(() => syncTrigger(false))
65
65
  </script>
66
66
 
67
67
  <template>
68
+ <!--
69
+ min-h-10 keeps the header a stable height whether or not a page fills the
70
+ action slots. Without it the row is sized by its tallest child — a bare
71
+ title is 32px but an action button is 40px — so the header, and the content
72
+ card below it, shifted by 8px between pages like Events and People.
73
+ A floor, not a fixed height: taller or wrapped content still grows.
74
+ -->
68
75
  <div
69
- class="flex flex-1 min-w-0 justify-between gap-5 md:gap-8 items-center"
76
+ class="flex flex-1 min-w-0 justify-between gap-5 md:gap-8 items-center min-h-10"
70
77
  >
71
78
  <div class="flex min-w-0 items-center gap-2">
72
79
  <div v-if="showLogo">
@@ -74,11 +81,13 @@ onBeforeUnmount(() => syncTrigger(false))
74
81
  :to="logoTo ? logoTo : '#'"
75
82
  class="grid items-center w-8 h-8"
76
83
  >
77
- <img
78
- :src="LivestormLogo"
79
- alt="Livestorm logo"
80
- class="w-6 h-6"
81
- >
84
+ <UiLogo
85
+ :height="1.5"
86
+ :width="1.5"
87
+ :themed="false"
88
+ :animated="false"
89
+ inverted="auto"
90
+ />
82
91
  </router-link>
83
92
  </div>
84
93
  <div
@@ -88,7 +97,7 @@ onBeforeUnmount(() => syncTrigger(false))
88
97
  <UiButton
89
98
  color="neutral"
90
99
  variant="tertiary"
91
- icon="i-ls-menu"
100
+ icon="i-ph-list"
92
101
  @click="toggle"
93
102
  />
94
103
  </div>
@@ -96,7 +105,7 @@ onBeforeUnmount(() => syncTrigger(false))
96
105
  <UiButton
97
106
  color="neutral"
98
107
  variant="tertiary"
99
- icon="i-ls-back"
108
+ icon="i-ph-arrow-left"
100
109
  />
101
110
  </div>
102
111
  <div
@@ -124,5 +133,16 @@ onBeforeUnmount(() => syncTrigger(false))
124
133
  >
125
134
  <slot name="slotB" />
126
135
  </div>
136
+
137
+ <!--
138
+ Teleport target for page-level actions, so a component deep inside the
139
+ page content (e.g. a settings section) can surface its primary action
140
+ here without threading props up. `contents` keeps the empty target out
141
+ of the flex layout, so pages that don't use it are unaffected.
142
+ -->
143
+ <div
144
+ id="ui-page-header-actions-teleport-target"
145
+ class="contents"
146
+ />
127
147
  </div>
128
148
  </template>
@@ -0,0 +1,99 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { PaginationEllipsis, PaginationList, PaginationListItem, PaginationNext, PaginationPrev, PaginationRoot } from 'reka-ui'
4
+ import { PAGINATION_ACTIVE_CLASSES, PAGINATION_IDLE_CLASSES, PAGINATION_RAIL_CLASSES } from './types'
5
+ import type { UiPaginationColor } from './types'
6
+
7
+ interface UiPaginationProps {
8
+ /** Controlled current page. Bind with `v-model:page`. */
9
+ page?: number
10
+ /** Initial page when uncontrolled. */
11
+ defaultPage?: number
12
+ /** Number of items per page. */
13
+ itemsPerPage?: number
14
+ /** Total number of items across all pages. */
15
+ total: number
16
+ /** Number of sibling pages shown around the current page. */
17
+ siblingCount?: number
18
+ disabled?: boolean
19
+ /** Always show the first and last page, with an ellipsis in between when needed. */
20
+ showEdges?: boolean
21
+ color?: UiPaginationColor
22
+ previousLabel?: string
23
+ nextLabel?: string
24
+ }
25
+
26
+ const props = withDefaults(defineProps<UiPaginationProps>(), {
27
+ page: undefined,
28
+ defaultPage: 1,
29
+ itemsPerPage: 10,
30
+ siblingCount: 1,
31
+ disabled: false,
32
+ showEdges: true,
33
+ color: 'neutral',
34
+ previousLabel: 'Previous page',
35
+ nextLabel: 'Next page',
36
+ })
37
+
38
+ defineEmits<{
39
+ 'update:page': [value: number]
40
+ }>()
41
+
42
+ const idleClasses = computed(() => PAGINATION_IDLE_CLASSES[props.color])
43
+ const activeClasses = computed(() => PAGINATION_ACTIVE_CLASSES[props.color])
44
+ const railClasses = computed(() => PAGINATION_RAIL_CLASSES[props.color])
45
+ </script>
46
+
47
+ <template>
48
+ <PaginationRoot
49
+ :page="page"
50
+ :default-page="defaultPage"
51
+ :items-per-page="itemsPerPage"
52
+ :total="total"
53
+ :sibling-count="siblingCount"
54
+ :disabled="disabled"
55
+ :show-edges="showEdges"
56
+ class="inline-flex items-center gap-2"
57
+ @update:page="$emit('update:page', $event)"
58
+ >
59
+ <PaginationPrev
60
+ class="flex h-10 shrink-0 items-center justify-center rounded-button-lg px-2.5 outline-none transition-colors focus-visible:focus-ring disabled:pointer-events-none disabled:opacity-50"
61
+ :class="idleClasses"
62
+ :aria-label="previousLabel"
63
+ >
64
+ <span class="i-ph-caret-left size-4" />
65
+ </PaginationPrev>
66
+
67
+ <PaginationList
68
+ v-slot="{ items }"
69
+ class="flex h-10 shrink-0 items-center gap-1 rounded-button-lg p-0.5"
70
+ :class="railClasses"
71
+ >
72
+ <template
73
+ v-for="(item, index) in items"
74
+ :key="item.type === 'page' ? `page-${item.value}` : `ellipsis-${index}`"
75
+ >
76
+ <PaginationListItem
77
+ v-if="item.type === 'page'"
78
+ :value="item.value"
79
+ class="flex h-full items-center justify-center rounded-[calc(var(--radius-button-lg)_-_2px)] px-3 text-action-button-md outline-none transition-colors focus-visible:focus-ring"
80
+ :class="[idleClasses, activeClasses]"
81
+ >
82
+ {{ item.value }}
83
+ </PaginationListItem>
84
+ <PaginationEllipsis
85
+ v-else
86
+ class="flex h-full items-center justify-center px-3 text-action-button-md text-actions-neutral-idle"
87
+ />
88
+ </template>
89
+ </PaginationList>
90
+
91
+ <PaginationNext
92
+ class="flex h-10 shrink-0 items-center justify-center rounded-button-lg px-2.5 outline-none transition-colors focus-visible:focus-ring disabled:pointer-events-none disabled:opacity-50"
93
+ :class="idleClasses"
94
+ :aria-label="nextLabel"
95
+ >
96
+ <span class="i-ph-caret-right size-4" />
97
+ </PaginationNext>
98
+ </PaginationRoot>
99
+ </template>
@@ -0,0 +1,2 @@
1
+ export { default as UiPagination } from './UiPagination.vue'
2
+ export type { UiPaginationColor } from './types'
@@ -0,0 +1,28 @@
1
+ export type UiPaginationColor = 'neutral' | 'neutral-invert' | 'primary' | 'success' | 'warning' | 'error'
2
+
3
+ export const PAGINATION_IDLE_CLASSES: Record<UiPaginationColor, string> = {
4
+ 'neutral': 'text-actions-neutral-idle hover:bg-actions-neutral-hover-overlay',
5
+ 'neutral-invert': 'text-actions-neutral-invert-idle-alpha-strong hover:bg-actions-neutral-invert-hover-overlay hover:text-actions-neutral-invert-idle',
6
+ 'primary': 'text-actions-primary-idle-alpha-strong hover:bg-actions-primary-hover-overlay hover:text-actions-primary-idle',
7
+ 'success': 'text-actions-success-idle-alpha-strong hover:bg-actions-success-hover-overlay hover:text-actions-success-idle',
8
+ 'warning': 'text-actions-warning-idle-alpha-strong hover:bg-actions-warning-hover-overlay hover:text-actions-warning-idle',
9
+ 'error': 'text-actions-danger-idle-alpha-strong hover:bg-actions-danger-hover-overlay hover:text-actions-danger-idle',
10
+ }
11
+
12
+ export const PAGINATION_ACTIVE_CLASSES: Record<UiPaginationColor, string> = {
13
+ 'neutral': 'data-[selected=true]:bg-actions-neutral-idle data-[selected=true]:text-actions-neutral-invert-idle data-[selected=true]:hover:bg-actions-neutral-idle',
14
+ 'neutral-invert': 'data-[selected=true]:bg-actions-neutral-invert-idle data-[selected=true]:text-actions-neutral-idle data-[selected=true]:hover:bg-actions-neutral-invert-idle',
15
+ 'primary': 'data-[selected=true]:bg-actions-primary-idle data-[selected=true]:text-actions-neutral-invert-idle data-[selected=true]:hover:bg-actions-primary-idle',
16
+ 'success': 'data-[selected=true]:bg-actions-success-idle data-[selected=true]:text-actions-neutral-invert-idle data-[selected=true]:hover:bg-actions-success-idle',
17
+ 'warning': 'data-[selected=true]:bg-actions-warning-idle data-[selected=true]:text-actions-neutral-invert-idle data-[selected=true]:hover:bg-actions-warning-idle',
18
+ 'error': 'data-[selected=true]:bg-actions-danger-idle data-[selected=true]:text-actions-neutral-invert-idle data-[selected=true]:hover:bg-actions-danger-idle',
19
+ }
20
+
21
+ export const PAGINATION_RAIL_CLASSES: Record<UiPaginationColor, string> = {
22
+ 'neutral': 'bg-actions-neutral-idle-alpha-light',
23
+ 'neutral-invert': 'bg-actions-neutral-invert-idle-alpha-light',
24
+ 'primary': 'bg-actions-primary-idle-alpha-light',
25
+ 'success': 'bg-actions-success-idle-alpha-light',
26
+ 'warning': 'bg-actions-warning-idle-alpha-light',
27
+ 'error': 'bg-actions-danger-idle-alpha-light',
28
+ }
@@ -0,0 +1,20 @@
1
+ <script setup lang="ts">
2
+ import type { DialogRootEmits, DialogRootProps } from 'reka-ui'
3
+ import { DialogRoot, useForwardPropsEmits } from 'reka-ui'
4
+
5
+ // A pane floats above the interface without an overlay, so it is non-modal by default.
6
+ const props = withDefaults(defineProps<DialogRootProps>(), {
7
+ open: undefined,
8
+ defaultOpen: false,
9
+ modal: false,
10
+ })
11
+ const emits = defineEmits<DialogRootEmits>()
12
+
13
+ const forwarded = useForwardPropsEmits(props, emits)
14
+ </script>
15
+
16
+ <template>
17
+ <DialogRoot v-bind="{ ...forwarded, ...$attrs }">
18
+ <slot />
19
+ </DialogRoot>
20
+ </template>