@ctrl-ui/react 0.8.7 → 0.8.9

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 (341) hide show
  1. package/dist/action-bar.d.ts +15 -7
  2. package/dist/action-bar.js +39 -19
  3. package/dist/activity.js +7 -15
  4. package/dist/audio-recorder.d.ts +14 -4
  5. package/dist/audio-recorder.js +82 -29
  6. package/dist/audio-visualizer.js +1 -1
  7. package/dist/blocks/coding-agent.js +39 -39
  8. package/dist/blocks/design-canvas.d.ts +5 -1
  9. package/dist/blocks/design-canvas.js +198 -92
  10. package/dist/blocks/discussion.d.ts +28 -0
  11. package/dist/blocks/discussion.js +67 -0
  12. package/dist/blocks/file-explorer-data.d.ts +1 -0
  13. package/dist/blocks/file-explorer-data.js +4 -1
  14. package/dist/blocks/file-explorer.d.ts +9 -1
  15. package/dist/blocks/file-explorer.js +461 -192
  16. package/dist/blocks/settings.d.ts +2 -1
  17. package/dist/blocks/settings.js +101 -79
  18. package/dist/blocks/team-chat.d.ts +12 -0
  19. package/dist/blocks/team-chat.js +26 -0
  20. package/dist/blocks/theme-toggle.js +23 -31
  21. package/dist/chat-composer-attachment.d.ts +4 -1
  22. package/dist/chat-composer-attachment.js +35 -10
  23. package/dist/chat-composer-editor/extensions/mention.d.ts +5 -1
  24. package/dist/chat-composer-editor/extensions/mention.js +59 -28
  25. package/dist/chat-composer-editor/ghost.js +2 -4
  26. package/dist/chat-composer-editor/types.d.ts +5 -0
  27. package/dist/chat-composer-editor.d.ts +1 -1
  28. package/dist/chat-composer-editor.js +57 -30
  29. package/dist/chat-composer.d.ts +13 -32
  30. package/dist/chat-composer.js +52 -12
  31. package/dist/chat-layout.d.ts +19 -0
  32. package/dist/chat-layout.js +91 -40
  33. package/dist/chat-message/social.d.ts +26 -0
  34. package/dist/chat-message/social.js +122 -0
  35. package/dist/chat-message.d.ts +28 -29
  36. package/dist/chat-message.js +106 -25
  37. package/dist/context.d.ts +2 -2
  38. package/dist/context.js +38 -23
  39. package/dist/dynamic-notification-canvas-loop.d.ts +24 -0
  40. package/dist/dynamic-notification-canvas-loop.js +113 -0
  41. package/dist/dynamic-notification-glass.js +23 -114
  42. package/dist/dynamic-notification-liquid.js +14 -118
  43. package/dist/dynamic-notification.d.ts +16 -8
  44. package/dist/dynamic-notification.js +85 -22
  45. package/dist/email/email-brand.d.ts +7 -4
  46. package/dist/email/email-brand.js +9 -6
  47. package/dist/email/email-code.d.ts +3 -2
  48. package/dist/email/email-code.js +11 -9
  49. package/dist/email/email-footer.d.ts +5 -3
  50. package/dist/email/email-footer.js +11 -5
  51. package/dist/email/email.d.ts +31 -9
  52. package/dist/email/email.js +50 -26
  53. package/dist/email/templates.d.ts +10 -9
  54. package/dist/email/templates.js +102 -38
  55. package/dist/email/theme.js +1 -1
  56. package/dist/environment-variables.d.ts +4 -2
  57. package/dist/environment-variables.js +52 -32
  58. package/dist/filter-bar/editor.js +20 -8
  59. package/dist/filter-bar/model.d.ts +1 -0
  60. package/dist/filter-bar/model.js +10 -1
  61. package/dist/filter-bar/motion.js +4 -4
  62. package/dist/filter-bar/parts.js +19 -8
  63. package/dist/filter-bar/types.d.ts +1 -0
  64. package/dist/filter-bar.js +19 -13
  65. package/dist/hooks/use-audio-recorder.js +12 -6
  66. package/dist/hooks/use-chat-composer.d.ts +19 -7
  67. package/dist/hooks/use-chat-composer.js +32 -5
  68. package/dist/hooks/use-chat-message.d.ts +28 -2
  69. package/dist/hooks/use-chat-message.js +41 -10
  70. package/dist/hooks/use-chat-thread-scroll.js +43 -7
  71. package/dist/hooks/use-color-area.d.ts +3 -3
  72. package/dist/hooks/use-color-area.js +13 -12
  73. package/dist/hooks/use-copy-to-clipboard.d.ts +2 -1
  74. package/dist/hooks/use-copy-to-clipboard.js +20 -20
  75. package/dist/hooks/use-dynamic-notification.d.ts +6 -2
  76. package/dist/hooks/use-dynamic-notification.js +26 -10
  77. package/dist/hooks/use-mobile.d.ts +3 -2
  78. package/dist/hooks/use-mobile.js +8 -4
  79. package/dist/hooks/use-textarea-trigger-menu.js +2 -1
  80. package/dist/hooks/use-trigger-menu.d.ts +54 -0
  81. package/dist/hooks/use-trigger-menu.js +102 -47
  82. package/dist/hooks/use-user-ask.d.ts +2 -0
  83. package/dist/hooks/use-user-ask.js +35 -41
  84. package/dist/inline-attachment.d.ts +6 -2
  85. package/dist/inline-attachment.js +36 -15
  86. package/dist/inline-citation.d.ts +12 -5
  87. package/dist/inline-citation.js +25 -16
  88. package/dist/knob-contracts/audio-recorder-knobs.d.ts +1 -1
  89. package/dist/knob-contracts/audio-recorder-knobs.js +2 -1
  90. package/dist/knob-contracts/audio-visualizer-knobs.d.ts +1 -1
  91. package/dist/knob-contracts/audio-visualizer-knobs.js +2 -1
  92. package/dist/knob-contracts/avatar-knobs.d.ts +1 -1
  93. package/dist/knob-contracts/avatar-knobs.js +6 -1
  94. package/dist/knob-contracts/badge-knobs.d.ts +1 -1
  95. package/dist/knob-contracts/badge-knobs.js +2 -1
  96. package/dist/knob-contracts/breadcrumb-knobs.d.ts +4 -0
  97. package/dist/knob-contracts/breadcrumb-knobs.js +8 -0
  98. package/dist/knob-contracts/button-knobs.d.ts +1 -1
  99. package/dist/knob-contracts/button-knobs.js +1 -0
  100. package/dist/knob-contracts/chat-message-knobs.d.ts +1 -1
  101. package/dist/knob-contracts/chat-message-knobs.js +8 -1
  102. package/dist/knob-contracts/code-diff-knobs.d.ts +1 -1
  103. package/dist/knob-contracts/code-diff-knobs.js +2 -0
  104. package/dist/knob-contracts/code-knobs.d.ts +1 -1
  105. package/dist/knob-contracts/code-knobs.js +2 -1
  106. package/dist/knob-contracts/dynamic-notification-knobs.d.ts +1 -1
  107. package/dist/knob-contracts/dynamic-notification-knobs.js +2 -0
  108. package/dist/knob-contracts/emoji-picker-knobs.d.ts +4 -0
  109. package/dist/knob-contracts/emoji-picker-knobs.js +10 -0
  110. package/dist/knob-contracts/field-knobs.d.ts +1 -1
  111. package/dist/knob-contracts/field-knobs.js +5 -1
  112. package/dist/knob-contracts/filter-bar-knobs.d.ts +1 -1
  113. package/dist/knob-contracts/filter-bar-knobs.js +1 -2
  114. package/dist/knob-contracts/markdown-knobs.d.ts +1 -1
  115. package/dist/knob-contracts/markdown-knobs.js +3 -1
  116. package/dist/knob-contracts/page-layout-knobs.d.ts +1 -1
  117. package/dist/knob-contracts/page-layout-knobs.js +2 -1
  118. package/dist/knob-contracts/popup-knobs.d.ts +1 -1
  119. package/dist/knob-contracts/popup-knobs.js +27 -0
  120. package/dist/knob-contracts/range-knobs.d.ts +1 -1
  121. package/dist/knob-contracts/range-knobs.js +1 -0
  122. package/dist/knob-contracts/rich-tooltip-knobs.d.ts +1 -1
  123. package/dist/knob-contracts/rich-tooltip-knobs.js +2 -1
  124. package/dist/knob-contracts/sidebar-knobs.d.ts +1 -1
  125. package/dist/knob-contracts/sidebar-knobs.js +27 -2
  126. package/dist/knob-contracts/spinner-knobs.d.ts +1 -1
  127. package/dist/knob-contracts/spinner-knobs.js +1 -1
  128. package/dist/knob-contracts/stepper-knobs.d.ts +1 -1
  129. package/dist/knob-contracts/stepper-knobs.js +4 -1
  130. package/dist/knob-contracts/switch-knobs.d.ts +1 -1
  131. package/dist/knob-contracts/switch-knobs.js +5 -0
  132. package/dist/knob-contracts/table-of-contents-knobs.d.ts +1 -1
  133. package/dist/knob-contracts/table-of-contents-knobs.js +2 -1
  134. package/dist/knob-contracts/tabs-knobs.d.ts +1 -1
  135. package/dist/knob-contracts/tabs-knobs.js +1 -0
  136. package/dist/knob-contracts/tree-knobs.d.ts +1 -1
  137. package/dist/knob-contracts/tree-knobs.js +2 -1
  138. package/dist/lib/cn.js +0 -4
  139. package/dist/lib/contrast.d.ts +1 -0
  140. package/dist/lib/contrast.js +17 -5
  141. package/dist/lib/dropzone-validation.js +41 -28
  142. package/dist/lib/env-file.d.ts +1 -1
  143. package/dist/lib/env-file.js +5 -5
  144. package/dist/lib/motion.d.ts +4 -0
  145. package/dist/lib/motion.js +15 -0
  146. package/dist/lib/trigger-detect.js +6 -8
  147. package/dist/markdown-block.js +2 -1
  148. package/dist/popup-parts.d.ts +8 -0
  149. package/dist/popup-parts.js +43 -0
  150. package/dist/source-badge.js +3 -1
  151. package/dist/styles/code.css +0 -2
  152. package/dist/styles/effects.css +6 -32
  153. package/dist/styles/index.css +10 -5
  154. package/dist/styles/recipes/accordion.css +9 -6
  155. package/dist/styles/recipes/action-bar.css +0 -15
  156. package/dist/styles/recipes/activity.css +5 -11
  157. package/dist/styles/recipes/alert.css +4 -4
  158. package/dist/styles/recipes/audio-recorder.css +15 -3
  159. package/dist/styles/recipes/audio-visualizer.css +13 -2
  160. package/dist/styles/recipes/avatar.css +78 -1
  161. package/dist/styles/recipes/badge.css +14 -4
  162. package/dist/styles/recipes/breadcrumb.css +77 -0
  163. package/dist/styles/recipes/button-group.css +5 -7
  164. package/dist/styles/recipes/button-states.css +66 -0
  165. package/dist/styles/recipes/button.css +24 -83
  166. package/dist/styles/recipes/calendar.css +17 -23
  167. package/dist/styles/recipes/card.css +2 -2
  168. package/dist/styles/recipes/chat-composer-attachment.css +9 -3
  169. package/dist/styles/recipes/chat-composer.css +7 -10
  170. package/dist/styles/recipes/chat-layout.css +35 -0
  171. package/dist/styles/recipes/chat-message-social.css +123 -0
  172. package/dist/styles/recipes/chat-message.css +157 -37
  173. package/dist/styles/recipes/choice.css +18 -21
  174. package/dist/styles/recipes/code-diff.css +46 -33
  175. package/dist/styles/recipes/code.css +36 -16
  176. package/dist/styles/recipes/collapsible.css +7 -2
  177. package/dist/styles/recipes/color-picker-details.css +3 -4
  178. package/dist/styles/recipes/color-picker-values.css +9 -8
  179. package/dist/styles/recipes/color-picker.css +11 -2
  180. package/dist/styles/recipes/context.css +3 -3
  181. package/dist/styles/recipes/dockable-panel.css +7 -4
  182. package/dist/styles/recipes/dropzone.css +7 -8
  183. package/dist/styles/recipes/dynamic-notification-input.css +8 -5
  184. package/dist/styles/recipes/dynamic-notification-motion.css +12 -21
  185. package/dist/styles/recipes/dynamic-notification.css +40 -26
  186. package/dist/styles/recipes/emoji-picker.css +132 -0
  187. package/dist/styles/recipes/empty.css +5 -4
  188. package/dist/styles/recipes/environment-variables.css +4 -15
  189. package/dist/styles/recipes/field-parts.css +30 -24
  190. package/dist/styles/recipes/field.css +61 -26
  191. package/dist/styles/recipes/filter-bar.css +18 -31
  192. package/dist/styles/recipes/gradient-editor.css +18 -7
  193. package/dist/styles/recipes/infinite-canvas.css +0 -1
  194. package/dist/styles/recipes/inline-attachment.css +3 -3
  195. package/dist/styles/recipes/inline-citation.css +10 -4
  196. package/dist/styles/recipes/item.css +23 -9
  197. package/dist/styles/recipes/markdown-block.css +5 -4
  198. package/dist/styles/recipes/markdown.css +68 -26
  199. package/dist/styles/recipes/morphing-panel.css +8 -8
  200. package/dist/styles/recipes/page-layout.css +20 -12
  201. package/dist/styles/recipes/pagination.css +4 -5
  202. package/dist/styles/recipes/phone-input.css +12 -8
  203. package/dist/styles/recipes/popup-controls.css +166 -33
  204. package/dist/styles/recipes/popup-drawer.css +3 -14
  205. package/dist/styles/recipes/popup-lists.css +128 -27
  206. package/dist/styles/recipes/popup-motion.css +4 -45
  207. package/dist/styles/recipes/popup-toast.css +55 -26
  208. package/dist/styles/recipes/popup.css +139 -141
  209. package/dist/styles/recipes/range.css +54 -18
  210. package/dist/styles/recipes/resizable.css +2 -7
  211. package/dist/styles/recipes/rich-tooltip.css +24 -34
  212. package/dist/styles/recipes/scroll-area.css +8 -1
  213. package/dist/styles/recipes/sidebar-menu.css +166 -34
  214. package/dist/styles/recipes/sidebar-parts.css +192 -0
  215. package/dist/styles/recipes/sidebar.css +188 -128
  216. package/dist/styles/recipes/source-badge.css +1 -1
  217. package/dist/styles/recipes/spinner.css +8 -1
  218. package/dist/styles/recipes/stepper.css +31 -13
  219. package/dist/styles/recipes/switch.css +68 -23
  220. package/dist/styles/recipes/table-of-contents.css +9 -1
  221. package/dist/styles/recipes/table.css +14 -10
  222. package/dist/styles/recipes/tabs.css +17 -12
  223. package/dist/styles/recipes/task-list.css +7 -14
  224. package/dist/styles/recipes/thread-rail.css +42 -19
  225. package/dist/styles/recipes/timeline.css +2 -0
  226. package/dist/styles/recipes/toolbar.css +27 -25
  227. package/dist/styles/recipes/track-highlight.css +10 -7
  228. package/dist/styles/recipes/tree.css +31 -4
  229. package/dist/styles/recipes/user-ask-details.css +0 -1
  230. package/dist/styles/recipes/user-ask.css +4 -4
  231. package/dist/styles/theme.css +37 -16
  232. package/dist/task-list.d.ts +4 -1
  233. package/dist/task-list.js +24 -8
  234. package/dist/thread-rail.d.ts +1 -1
  235. package/dist/thread-rail.js +61 -19
  236. package/dist/transcript-divider.js +2 -1
  237. package/dist/ui/alert.d.ts +3 -1
  238. package/dist/ui/alert.js +14 -8
  239. package/dist/ui/autocomplete.js +3 -1
  240. package/dist/ui/avatar.d.ts +8 -0
  241. package/dist/ui/avatar.js +23 -3
  242. package/dist/ui/badge.js +1 -1
  243. package/dist/ui/breadcrumb.d.ts +27 -0
  244. package/dist/ui/breadcrumb.js +88 -0
  245. package/dist/ui/button.d.ts +2 -1
  246. package/dist/ui/button.js +1 -1
  247. package/dist/ui/calendar.js +2 -2
  248. package/dist/ui/card.d.ts +3 -1
  249. package/dist/ui/card.js +12 -7
  250. package/dist/ui/checkbox.d.ts +2 -0
  251. package/dist/ui/code-diff.d.ts +1 -1
  252. package/dist/ui/code-diff.js +71 -27
  253. package/dist/ui/code.d.ts +5 -3
  254. package/dist/ui/code.js +26 -27
  255. package/dist/ui/color-picker.d.ts +5 -2
  256. package/dist/ui/color-picker.js +84 -36
  257. package/dist/ui/combobox-disabled-selection.d.ts +1 -2
  258. package/dist/ui/combobox-disabled-selection.js +7 -9
  259. package/dist/ui/combobox.js +13 -5
  260. package/dist/ui/command.d.ts +8 -2
  261. package/dist/ui/command.js +30 -14
  262. package/dist/ui/context-menu.js +3 -0
  263. package/dist/ui/dialog.d.ts +2 -1
  264. package/dist/ui/dialog.js +3 -22
  265. package/dist/ui/dockable-panel-geometry.d.ts +1 -0
  266. package/dist/ui/dockable-panel-geometry.js +7 -1
  267. package/dist/ui/dockable-panel.d.ts +9 -6
  268. package/dist/ui/dockable-panel.js +63 -23
  269. package/dist/ui/dropdown-menu.js +4 -3
  270. package/dist/ui/dropzone.d.ts +2 -2
  271. package/dist/ui/dropzone.js +75 -16
  272. package/dist/ui/emoji-picker.d.ts +18 -0
  273. package/dist/ui/emoji-picker.js +136 -0
  274. package/dist/ui/empty.d.ts +3 -1
  275. package/dist/ui/empty.js +12 -7
  276. package/dist/ui/gradient-editor.d.ts +1 -1
  277. package/dist/ui/gradient-editor.js +37 -21
  278. package/dist/ui/infinite-canvas.d.ts +3 -2
  279. package/dist/ui/infinite-canvas.js +63 -19
  280. package/dist/ui/input-group.d.ts +0 -3
  281. package/dist/ui/input-group.js +3 -13
  282. package/dist/ui/input-otp.d.ts +1 -1
  283. package/dist/ui/input-otp.js +32 -15
  284. package/dist/ui/input.js +4 -4
  285. package/dist/ui/item.d.ts +2 -1
  286. package/dist/ui/item.js +11 -7
  287. package/dist/ui/live-status.d.ts +7 -0
  288. package/dist/ui/live-status.js +17 -0
  289. package/dist/ui/markdown-elements.d.ts +6 -0
  290. package/dist/ui/markdown-elements.js +31 -1
  291. package/dist/ui/menubar.js +2 -2
  292. package/dist/ui/morphing-panel.js +2 -1
  293. package/dist/ui/native-select.d.ts +1 -1
  294. package/dist/ui/native-select.js +24 -11
  295. package/dist/ui/page-layout.d.ts +2 -2
  296. package/dist/ui/page-layout.js +2 -4
  297. package/dist/ui/pagination.d.ts +7 -5
  298. package/dist/ui/pagination.js +19 -13
  299. package/dist/ui/phone-input.d.ts +1 -0
  300. package/dist/ui/phone-input.js +56 -58
  301. package/dist/ui/radio-group.d.ts +2 -0
  302. package/dist/ui/resizable.d.ts +3 -2
  303. package/dist/ui/resizable.js +5 -3
  304. package/dist/ui/responsive-dialog.d.ts +1 -1
  305. package/dist/ui/responsive-dialog.js +4 -21
  306. package/dist/ui/rich-tooltip.d.ts +10 -4
  307. package/dist/ui/rich-tooltip.js +81 -33
  308. package/dist/ui/scroll-area.d.ts +1 -1
  309. package/dist/ui/scroll-area.js +43 -28
  310. package/dist/ui/sidebar-menu.d.ts +5 -1
  311. package/dist/ui/sidebar-menu.js +34 -34
  312. package/dist/ui/sidebar-provider.d.ts +10 -1
  313. package/dist/ui/sidebar-provider.js +38 -13
  314. package/dist/ui/sidebar-resize-rail.js +7 -3
  315. package/dist/ui/sidebar.d.ts +11 -6
  316. package/dist/ui/sidebar.js +48 -24
  317. package/dist/ui/slider.js +31 -22
  318. package/dist/ui/spinner.d.ts +3 -2
  319. package/dist/ui/spinner.js +3 -2
  320. package/dist/ui/stepper.d.ts +8 -1
  321. package/dist/ui/stepper.js +74 -25
  322. package/dist/ui/switch.d.ts +1 -0
  323. package/dist/ui/table-of-contents.d.ts +2 -2
  324. package/dist/ui/table-of-contents.js +20 -8
  325. package/dist/ui/table.js +4 -2
  326. package/dist/ui/textarea.d.ts +1 -1
  327. package/dist/ui/textarea.js +20 -9
  328. package/dist/ui/timeline.d.ts +3 -1
  329. package/dist/ui/timeline.js +17 -9
  330. package/dist/ui/toast.d.ts +11 -5
  331. package/dist/ui/toast.js +100 -54
  332. package/dist/ui/toolbar.d.ts +2 -1
  333. package/dist/ui/toolbar.js +3 -1
  334. package/dist/ui/tooltip.js +3 -20
  335. package/dist/ui/tree.js +120 -80
  336. package/dist/ui/trigger-menu.js +1 -0
  337. package/dist/user-ask.d.ts +10 -5
  338. package/dist/user-ask.js +31 -11
  339. package/package.json +5 -1
  340. package/dist/hooks/use-keyboard-navigation.d.ts +0 -3
  341. package/dist/hooks/use-keyboard-navigation.js +0 -22
@@ -28,6 +28,9 @@ function SidebarResizeRail({ className, ref, onPointerDown, onClick, onKeyDown,
28
28
  function clampWidth(nextWidth) {
29
29
  return Math.min(maxWidth, Math.max(minWidth, nextWidth));
30
30
  }
31
+ function anchoredToLeftEdge(element) {
32
+ return side === "left" === (getComputedStyle(element).direction !== "rtl");
33
+ }
31
34
  function handlePointerDown(event) {
32
35
  onPointerDown?.(event);
33
36
  if (event.defaultPrevented || event.button !== 0) return;
@@ -43,6 +46,7 @@ function SidebarResizeRail({ className, ref, onPointerDown, onClick, onKeyDown,
43
46
  handle.setPointerCapture(event.pointerId);
44
47
  const pointerId = event.pointerId;
45
48
  const startX = event.clientX;
49
+ const anchoredLeft = anchoredToLeftEdge(handle);
46
50
  const wrapperBounds = wrapper.getBoundingClientRect();
47
51
  const initialWidth = measure.getBoundingClientRect().width;
48
52
  const previousWidth = wrapper.style.getPropertyValue("--sidebar-width");
@@ -88,7 +92,7 @@ function SidebarResizeRail({ className, ref, onPointerDown, onClick, onKeyDown,
88
92
  document.body.style.userSelect = "none";
89
93
  wrapper.setAttribute("data-resizing", "true");
90
94
  handle.setAttribute("data-resizing", "true");
91
- const cursorWidth = side === "left" ? pointer.clientX - wrapperBounds.left : wrapperBounds.right - pointer.clientX;
95
+ const cursorWidth = anchoredLeft ? pointer.clientX - wrapperBounds.left : wrapperBounds.right - pointer.clientX;
92
96
  if (updateCollapse(cursorWidth)) return;
93
97
  draftWidth = clampWidth(cursorWidth);
94
98
  wrapper.style.setProperty("--sidebar-width", `${draftWidth}px`);
@@ -152,7 +156,7 @@ function SidebarResizeRail({ className, ref, onPointerDown, onClick, onKeyDown,
152
156
  case "ArrowLeft":
153
157
  case "ArrowRight":
154
158
  event.preventDefault();
155
- resizeWithArrow(event.key === (side === "left" ? "ArrowRight" : "ArrowLeft"));
159
+ resizeWithArrow(event.key === (anchoredToLeftEdge(event.currentTarget) ? "ArrowRight" : "ArrowLeft"));
156
160
  return;
157
161
  }
158
162
  }
@@ -179,7 +183,7 @@ function SidebarResizeRail({ className, ref, onPointerDown, onClick, onKeyDown,
179
183
  onPointerDown: handlePointerDown,
180
184
  onClick: handleClick,
181
185
  onKeyDown: handleKeyDown,
182
- className: cn("absolute inset-y-0 z-20 hidden cursor-col-resize touch-pan-y outline-hidden group-data-[side=left]:-right-1 group-data-[side=right]:-left-1 group-data-[side=left]:group-data-[collapsible=offcanvas]:-right-2 group-data-[side=right]:group-data-[collapsible=offcanvas]:-left-2 lg:block", className),
186
+ className: cn("absolute inset-y-0 z-20 hidden cursor-col-resize touch-pan-y group-data-[side=left]:-end-1 group-data-[side=right]:-start-1 group-data-[side=left]:group-data-[collapsible=offcanvas]:-end-2 group-data-[side=right]:group-data-[collapsible=offcanvas]:-start-2 lg:block", className),
183
187
  children: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
184
188
  ref: widthMeasureRef,
185
189
  "aria-hidden": true,
@@ -1,9 +1,10 @@
1
+ import { RenderProp } from "../control-props.js";
1
2
  import { Button } from "./button.js";
2
3
  import { SidebarKnobStyle } from "../knob-contracts/sidebar-knobs.js";
3
4
  import { SidebarLayout } from "../skin.js";
4
- import { SidebarGroup, SidebarGroupContent, SidebarGroupContentProps, SidebarGroupLabel, SidebarGroupLabelProps, SidebarMenu, SidebarMenuAction, SidebarMenuActionProps, SidebarMenuButton, SidebarMenuButtonProps, SidebarMenuButtonSize, SidebarMenuButtonVariant, SidebarMenuItem, SidebarMenuSub, SidebarSelectionIndicator, sidebarMenuButtonSizes, sidebarMenuButtonVariants } from "./sidebar-menu.js";
5
- import { SidebarProvider, SidebarProviderProps, SidebarStyle, useSidebar } from "./sidebar-provider.js";
6
5
  import { SidebarResizeRailProps } from "./sidebar-resize-rail.js";
6
+ import { SidebarGroup, SidebarGroupContent, SidebarGroupContentProps, SidebarGroupLabel, SidebarGroupLabelProps, SidebarMenu, SidebarMenuAction, SidebarMenuActionProps, SidebarMenuBadge, SidebarMenuBadgeProps, SidebarMenuButton, SidebarMenuButtonProps, SidebarMenuButtonSize, SidebarMenuButtonVariant, SidebarMenuItem, SidebarMenuSub, SidebarSelectionIndicator, sidebarMenuButtonSizes, sidebarMenuButtonVariants } from "./sidebar-menu.js";
7
+ import { SidebarLayoutMode, SidebarProvider, SidebarProviderProps, SidebarStyle, useSidebar } from "./sidebar-provider.js";
7
8
  import { CSSProperties, ComponentProps } from "react";
8
9
  //#region src/ui/sidebar.d.ts
9
10
  export type SidebarRailProps = (Omit<ComponentProps<"button">, "style"> & {
@@ -12,7 +13,8 @@ export type SidebarRailProps = (Omit<ComponentProps<"button">, "style"> & {
12
13
  }) | (SidebarResizeRailProps & {
13
14
  resizable: true;
14
15
  });
15
- export type SidebarInsetProps = Omit<ComponentProps<"main">, "style"> & {
16
+ export type SidebarInsetProps = Omit<ComponentProps<"main">, "style" | "id"> & {
17
+ render?: RenderProp<ComponentProps<"main">>;
16
18
  style?: CSSProperties & SidebarKnobStyle;
17
19
  };
18
20
  type SidebarSurfaceStyle = CSSProperties & SidebarKnobStyle;
@@ -20,12 +22,15 @@ export type SidebarProps = Omit<ComponentProps<"div">, "style"> & {
20
22
  side?: "left" | "right";
21
23
  variant?: SidebarLayout;
22
24
  collapsible?: "offcanvas" | "icon" | "none";
25
+ label?: string;
23
26
  style?: SidebarSurfaceStyle;
24
27
  };
25
28
  export declare function Sidebar({ side, collapsible, ...props }: SidebarProps): import("react").JSX.Element;
26
- export declare function SidebarTrigger({ className, onClick, size, ref, ...props }: ComponentProps<typeof Button>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
29
+ export declare function SidebarTrigger({ className, onClick, size, label, ref, ...props }: ComponentProps<typeof Button> & {
30
+ label?: string;
31
+ }): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
27
32
  export declare function SidebarRail(props: SidebarRailProps): any;
28
- export declare function SidebarInset({ className, ...props }: SidebarInsetProps): import("react").JSX.Element;
33
+ export declare function SidebarInset({ className, render, ref, ...props }: SidebarInsetProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
29
34
  export declare function SidebarHeader({ className, ...props }: ComponentProps<"div"> & {
30
35
  style?: CSSProperties & SidebarKnobStyle;
31
36
  }): import("react").JSX.Element;
@@ -34,4 +39,4 @@ export declare function SidebarFooter({ className, ...props }: ComponentProps<"d
34
39
  }): import("react").JSX.Element;
35
40
  export declare function SidebarContent({ className, children, ...props }: ComponentProps<"div">): import("react").JSX.Element;
36
41
  //#endregion
37
- export { SidebarGroup, SidebarGroupContent, type SidebarGroupContentProps, SidebarGroupLabel, type SidebarGroupLabelProps, SidebarMenu, SidebarMenuAction, type SidebarMenuActionProps, SidebarMenuButton, type SidebarMenuButtonProps, type SidebarMenuButtonSize, type SidebarMenuButtonVariant, SidebarMenuItem, SidebarMenuSub, SidebarProvider, type SidebarProviderProps, type SidebarSelectionIndicator, type SidebarStyle, sidebarMenuButtonSizes, sidebarMenuButtonVariants, useSidebar };
42
+ export { SidebarGroup, SidebarGroupContent, type SidebarGroupContentProps, SidebarGroupLabel, type SidebarGroupLabelProps, type SidebarLayoutMode, SidebarMenu, SidebarMenuAction, type SidebarMenuActionProps, SidebarMenuBadge, type SidebarMenuBadgeProps, SidebarMenuButton, type SidebarMenuButtonProps, type SidebarMenuButtonSize, type SidebarMenuButtonVariant, SidebarMenuItem, SidebarMenuSub, SidebarProvider, type SidebarProviderProps, type SidebarSelectionIndicator, type SidebarStyle, sidebarMenuButtonSizes, sidebarMenuButtonVariants, useSidebar };
@@ -3,10 +3,10 @@ import { cn } from "../lib/cn.js";
3
3
  import { useSkin } from "../skin-provider.js";
4
4
  import { Button } from "./button.js";
5
5
  import { ScrollArea } from "./scroll-area.js";
6
- import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from "./sheet.js";
6
+ import { Sheet, SheetContent, SheetHeader, SheetTitle } from "./sheet.js";
7
7
  import { SidebarProvider, SidebarSurfaceContext, useSidebar, useSidebarElements, useSidebarSurface } from "./sidebar-provider.js";
8
8
  import { SidebarResizeRail } from "./sidebar-resize-rail.js";
9
- import { SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarMenu, SidebarMenuAction, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, sidebarMenuButtonSizes, sidebarMenuButtonVariants } from "./sidebar-menu.js";
9
+ import { SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, sidebarMenuButtonSizes, sidebarMenuButtonVariants } from "./sidebar-menu.js";
10
10
  import { useState } from "react";
11
11
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
12
12
  import { PanelLeftIcon } from "lucide-react";
@@ -34,22 +34,23 @@ function Sidebar({ side = "left", collapsible = "offcanvas", ...props }) {
34
34
  })
35
35
  });
36
36
  }
37
- function SidebarSurface({ variant, ref, className, children, style, railContainerRef, ...props }) {
37
+ function SidebarSurface({ variant, label = "Navigation", ref, className, children, style, railContainerRef, ...props }) {
38
38
  const { side, collapsible } = useSidebarSurface();
39
- const { offcanvasRef } = useSidebarElements();
39
+ const { offcanvasRef, triggerRef } = useSidebarElements();
40
40
  const skin = useSkin();
41
- const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
41
+ const { isMobile, state, openMobile, setOpenMobile, layout, sidebarId } = useSidebar();
42
42
  const resolvedVariant = variant ?? skin.sidebarLayout ?? "sidebar";
43
43
  const container = useRender({
44
44
  defaultTagName: "div",
45
45
  ref: collapsible === "offcanvas" ? [ref ?? null, offcanvasRef] : ref,
46
46
  props: {
47
+ id: sidebarId,
47
48
  ...props,
48
49
  "data-control-ui": "sidebar",
49
50
  "data-control-family": "sidebar",
50
51
  "data-slot": "container",
51
52
  tabIndex: -1,
52
- className: cn("fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) lg:flex", side === "left" ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]" : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]", resolvedVariant === "floating" || resolvedVariant === "inset" ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon)", className),
53
+ className: cn(layout === "contained" ? "absolute h-full" : "fixed h-svh", "inset-y-0 z-10 hidden w-(--sidebar-width) lg:flex", side === "left" ? "start-0" : "end-0", resolvedVariant === "floating" || resolvedVariant === "inset" ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon)", className),
53
54
  children: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
54
55
  "data-control-ui": "sidebar",
55
56
  "data-control-family": "sidebar",
@@ -89,13 +90,15 @@ function SidebarSurface({ variant, ref, className, children, style, railContaine
89
90
  onOpenChange: setOpenMobile,
90
91
  children: /* @__PURE__ */ jsxs(SheetContent, {
91
92
  side,
93
+ finalFocus: triggerRef,
92
94
  className: "w-(--sidebar-width) gap-0 p-0",
93
95
  style: mobileSheetStyle,
94
- children: [/* @__PURE__ */ jsxs(SheetHeader, {
96
+ children: [/* @__PURE__ */ jsx(SheetHeader, {
95
97
  className: "sr-only",
96
- children: [/* @__PURE__ */ jsx(SheetTitle, { children: "Sidebar" }), /* @__PURE__ */ jsx(SheetDescription, { children: "Displays the mobile sidebar." })]
98
+ children: /* @__PURE__ */ jsx(SheetTitle, { children: label })
97
99
  }), /* @__PURE__ */ jsx("div", {
98
100
  ref,
101
+ id: sidebarId,
99
102
  "data-control-ui": "sidebar",
100
103
  "data-control-family": "sidebar",
101
104
  "data-slot": "root",
@@ -136,9 +139,10 @@ function SidebarSurface({ variant, ref, className, children, style, railContaine
136
139
  }), container]
137
140
  });
138
141
  }
139
- function SidebarTrigger({ className, onClick, size = "sm", ref, ...props }) {
140
- const { toggleSidebar, isMobile, openMobile, open } = useSidebar();
142
+ function SidebarTrigger({ className, onClick, size = "sm", label = "Toggle sidebar", ref, ...props }) {
143
+ const { toggleSidebar, isMobile, openMobile, open, keyboardShortcut, sidebarId } = useSidebar();
141
144
  const { triggerRef, railRef } = useSidebarElements();
145
+ const shortcutKey = keyboardShortcut?.toUpperCase();
142
146
  return useRender({
143
147
  defaultTagName: "button",
144
148
  render: /* @__PURE__ */ jsx(Button, {
@@ -152,16 +156,26 @@ function SidebarTrigger({ className, onClick, size = "sm", ref, ...props }) {
152
156
  "data-slot": "trigger",
153
157
  "data-sidebar-trigger": "",
154
158
  "aria-expanded": isMobile ? openMobile : open,
159
+ "aria-controls": isMobile && !openMobile ? void 0 : sidebarId,
160
+ "aria-keyshortcuts": shortcutKey ? `Meta+${shortcutKey} Control+${shortcutKey}` : void 0,
155
161
  className: cn(sidebarTriggerWidth[size], "px-0", className),
156
162
  onClick: (event) => {
157
163
  onClick?.(event);
158
164
  if (event.defaultPrevented) return;
159
165
  toggleSidebar();
160
- if (!isMobile && !open) railRef.current?.focus({ preventScroll: true });
166
+ if (isMobile || open) return;
167
+ const trigger = event.currentTarget;
168
+ requestAnimationFrame(() => {
169
+ if (!trigger.checkVisibility()) railRef.current?.focus({ preventScroll: true });
170
+ });
161
171
  },
162
- children: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(PanelLeftIcon, { className: "size-4" }), /* @__PURE__ */ jsx("span", {
172
+ children: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(PanelLeftIcon, {
173
+ className: "size-4",
174
+ "data-icon-dir": "inline",
175
+ "aria-hidden": "true"
176
+ }), /* @__PURE__ */ jsx("span", {
163
177
  className: "sr-only",
164
- children: "Toggle Sidebar"
178
+ children: label
165
179
  })] })
166
180
  }
167
181
  });
@@ -189,25 +203,34 @@ function SidebarToggleRail({ className, ref, onClick, ...props }) {
189
203
  "data-control-ui": "sidebar",
190
204
  "data-control-family": "sidebar",
191
205
  "data-slot": "rail",
192
- "aria-label": props["aria-label"] ?? "Toggle Sidebar",
206
+ "aria-label": props["aria-label"] ?? "Toggle sidebar",
193
207
  "aria-expanded": open,
194
208
  tabIndex: -1,
195
209
  onClick: (event) => {
196
210
  onClick?.(event);
197
211
  if (!event.defaultPrevented) toggleSidebar();
198
212
  },
199
- title: "Toggle Sidebar",
200
- className: cn("absolute inset-y-0 z-20 hidden -translate-x-1/2 group-data-[side=left]:-right-4 group-data-[side=right]:left-0 lg:flex", "in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize", "[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize", "group-data-[collapsible=offcanvas]:translate-x-0", className)
213
+ title: props.title ?? "Toggle sidebar",
214
+ className: cn("absolute inset-y-0 z-20 hidden -translate-x-1/2 rtl:translate-x-1/2 group-data-[side=left]:-end-4 group-data-[side=right]:start-0 lg:flex", "in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize", "[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize", "group-data-[collapsible=offcanvas]:translate-x-0", className)
201
215
  }
202
216
  });
203
217
  }
204
- function SidebarInset({ className, ...props }) {
205
- return /* @__PURE__ */ jsx("main", {
206
- "data-control-ui": "sidebar",
207
- "data-control-family": "sidebar",
208
- "data-slot": "inset",
209
- className: cn("relative flex min-w-0 w-full flex-1 flex-col", className),
210
- ...props
218
+ function SidebarInset({ className, render, ref, ...props }) {
219
+ const { layout, contentId } = useSidebar();
220
+ const { insetRef } = useSidebarElements();
221
+ return useRender({
222
+ defaultTagName: layout === "contained" ? "div" : "main",
223
+ render,
224
+ ref: [ref ?? null, insetRef],
225
+ props: {
226
+ tabIndex: -1,
227
+ ...props,
228
+ id: contentId,
229
+ "data-control-ui": "sidebar",
230
+ "data-control-family": "sidebar",
231
+ "data-slot": "inset",
232
+ className: cn("relative flex min-w-0 w-full flex-1 flex-col", className)
233
+ }
211
234
  });
212
235
  }
213
236
  function SidebarHeader({ className, ...props }) {
@@ -234,6 +257,7 @@ function SidebarContent({ className, children, ...props }) {
234
257
  "data-slot": "content",
235
258
  className: cn("min-h-0 flex-1 group-data-[collapsible=icon]:overflow-hidden", className),
236
259
  lockAxis: "x",
260
+ viewportClassName: "overscroll-contain",
237
261
  ...props,
238
262
  children: /* @__PURE__ */ jsx("div", {
239
263
  "data-control-ui": "sidebar",
@@ -245,4 +269,4 @@ function SidebarContent({ className, children, ...props }) {
245
269
  });
246
270
  }
247
271
  //#endregion
248
- export { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarProvider, SidebarRail, SidebarTrigger, sidebarMenuButtonSizes, sidebarMenuButtonVariants, useSidebar };
272
+ export { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarProvider, SidebarRail, SidebarTrigger, sidebarMenuButtonSizes, sidebarMenuButtonVariants, useSidebar };
package/dist/ui/slider.js CHANGED
@@ -12,10 +12,10 @@ function tickPositions(min, max, step) {
12
12
  if (stepCount <= 1 || stepCount > MAX_VISIBLE_STEP_TICKS) return [];
13
13
  return Array.from({ length: stepCount - 1 }, (_, i) => (i + 1) / stepCount * 100);
14
14
  }
15
- function displaySliderValue(values, formatValue) {
15
+ function displaySliderValue(formattedValues, values, formatValue) {
16
16
  const value = values[0];
17
17
  if (value === void 0) return "";
18
- return formatValue ? formatValue(value) : Math.round(value);
18
+ return formatValue ? formatValue(value) : formattedValues[0] ?? "";
19
19
  }
20
20
  function Slider({ className, variant = "default", label, showValue, formatValue, value, defaultValue, onValueChange, onValueCommitted, orientation = "horizontal", min = 0, max = 100, step, disabled, style, "aria-label": ariaLabel, ...props }) {
21
21
  const showValueResolved = showValue ?? Boolean(label);
@@ -78,38 +78,47 @@ function Slider({ className, variant = "default", label, showValue, formatValue,
78
78
  }
79
79
  }, pct)),
80
80
  /* @__PURE__ */ jsx(Slider$1.Thumb, {
81
- "aria-label": ariaLabel,
81
+ "aria-label": ariaLabel ?? (labeled ? void 0 : label),
82
+ getAriaValueText: formatValue ? (_, thumbValue) => formatValue(thumbValue) : void 0,
82
83
  "data-control-ui": "slider",
83
84
  "data-control-family": "range",
84
85
  "data-range-kind": "slider",
85
86
  "data-slot": "thumb",
86
87
  "data-variant": variant,
87
- className: cn("block", variant === "plain" && "after:absolute after:-inset-3 after:content-['']"),
88
+ "data-focus-ring": "within",
89
+ className: "block",
88
90
  style
89
91
  })
90
92
  ]
91
93
  })
92
- }), labeled && /* @__PURE__ */ jsxs("div", {
94
+ }), labeled && /* @__PURE__ */ jsx(SliderLabelOverlay, {
95
+ label,
96
+ showValue: showValueResolved,
97
+ formatValue
98
+ })]
99
+ });
100
+ }
101
+ function SliderLabelOverlay({ label, showValue, formatValue }) {
102
+ return /* @__PURE__ */ jsxs("div", {
103
+ "data-control-ui": "slider",
104
+ "data-control-family": "range",
105
+ "data-range-kind": "slider",
106
+ "data-slot": "label-overlay",
107
+ className: "pointer-events-none absolute inset-0 z-10 flex items-center justify-between",
108
+ children: [label ? /* @__PURE__ */ jsx(Slider$1.Label, {
93
109
  "data-control-ui": "slider",
94
110
  "data-control-family": "range",
95
111
  "data-range-kind": "slider",
96
- "data-slot": "label-overlay",
97
- className: "pointer-events-none absolute inset-0 z-10 flex items-center justify-between",
98
- children: [label ? /* @__PURE__ */ jsx(Slider$1.Label, {
99
- "data-control-ui": "slider",
100
- "data-control-family": "range",
101
- "data-range-kind": "slider",
102
- "data-slot": "label",
103
- className: "select-none",
104
- children: label
105
- }) : /* @__PURE__ */ jsx("span", {}), showValueResolved && /* @__PURE__ */ jsx(Slider$1.Value, {
106
- "data-control-ui": "slider",
107
- "data-control-family": "range",
108
- "data-range-kind": "slider",
109
- "data-slot": "value",
110
- className: "select-none",
111
- children: (_, values) => displaySliderValue(values, formatValue)
112
- })]
112
+ "data-slot": "label",
113
+ className: "select-none",
114
+ children: label
115
+ }) : /* @__PURE__ */ jsx("span", {}), showValue && /* @__PURE__ */ jsx(Slider$1.Value, {
116
+ "data-control-ui": "slider",
117
+ "data-control-family": "range",
118
+ "data-range-kind": "slider",
119
+ "data-slot": "value",
120
+ className: "select-none",
121
+ children: (formattedValues, values) => displaySliderValue(formattedValues, values, formatValue)
113
122
  })]
114
123
  });
115
124
  }
@@ -4,8 +4,9 @@ import { CSSProperties, ComponentProps } from "react";
4
4
  //#region src/ui/spinner.d.ts
5
5
  export type SpinnerProps = Omit<ComponentProps<"span"> & {
6
6
  size?: ControlSize;
7
- }, "style"> & {
7
+ label?: string;
8
+ }, "style" | "children"> & {
8
9
  style?: CSSProperties & SpinnerKnobStyle;
9
10
  };
10
- export declare function Spinner({ size, className, ...props }: SpinnerProps): import("react").JSX.Element;
11
+ export declare function Spinner({ size, label, className, ...props }: SpinnerProps): import("react").JSX.Element;
11
12
  //#endregion
@@ -2,12 +2,13 @@ import { cn } from "../lib/cn.js";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { Loader2 } from "lucide-react";
4
4
  //#region src/ui/spinner.tsx
5
- function Spinner({ size = "sm", className, ...props }) {
5
+ function Spinner({ size = "sm", label = "Loading", className, ...props }) {
6
6
  return /* @__PURE__ */ jsxs("span", {
7
7
  role: "status",
8
8
  "data-control-ui": "spinner",
9
9
  "data-control-family": "spinner",
10
10
  "data-slot": "root",
11
+ "data-motion-essential": "",
11
12
  className: cn("inline-flex", className),
12
13
  ...props,
13
14
  children: [/* @__PURE__ */ jsx(Loader2, {
@@ -18,7 +19,7 @@ function Spinner({ size = "sm", className, ...props }) {
18
19
  "data-size": size
19
20
  }), /* @__PURE__ */ jsx("span", {
20
21
  className: "sr-only",
21
- children: "Loading"
22
+ children: label
22
23
  })]
23
24
  });
24
25
  }
@@ -4,6 +4,12 @@ import { CSSProperties, ComponentProps } from "react";
4
4
  export type StepperOrientation = "horizontal" | "vertical";
5
5
  export type StepperContentMode = "current" | "all";
6
6
  export type StepperState = "neutral" | "complete" | "current" | "upcoming";
7
+ export type StepperStatusDetails = {
8
+ step: number;
9
+ state: StepperState;
10
+ invalid: boolean;
11
+ disabled: boolean;
12
+ };
7
13
  export type StepperProps = Omit<ComponentProps<"div">, "defaultValue" | "onChange"> & {
8
14
  value?: number | null;
9
15
  defaultValue?: number | null;
@@ -11,6 +17,7 @@ export type StepperProps = Omit<ComponentProps<"div">, "defaultValue" | "onChang
11
17
  orientation?: StepperOrientation;
12
18
  contentMode?: StepperContentMode;
13
19
  responsive?: boolean;
20
+ getStatusLabel?: (details: StepperStatusDetails) => string;
14
21
  } & {
15
22
  style?: CSSProperties & StepperKnobStyle;
16
23
  };
@@ -45,7 +52,7 @@ export type StepperContentProps = ComponentProps<"section"> & {
45
52
  } & {
46
53
  style?: CSSProperties & StepperKnobStyle;
47
54
  };
48
- export declare function Stepper({ value, defaultValue, onValueChange, orientation, contentMode, responsive, id, className, children, ...props }: StepperProps): import("react").JSX.Element;
55
+ export declare function Stepper({ value, defaultValue, onValueChange, orientation, contentMode, responsive, getStatusLabel, id, className, children, ...props }: StepperProps): import("react").JSX.Element;
49
56
  export declare function StepperList({ className, ...props }: StepperListProps): import("react").JSX.Element;
50
57
  export declare function StepperItem({ step, disabled, invalid, className, children, ...props }: StepperItemProps): import("react").JSX.Element;
51
58
  export declare function StepperTrigger({ disabled: disabledProp, className, onClick, type, ...props }: StepperTriggerProps): import("react").JSX.Element;
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/cn.js";
3
- import { createContext, useContext, useId, useState } from "react";
3
+ import { createContext, useContext, useEffect, useId, useState } from "react";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  import { CheckIcon, CircleAlertIcon } from "lucide-react";
6
6
  //#region src/ui/stepper.tsx
@@ -16,25 +16,48 @@ function useStepperItem() {
16
16
  if (!context) throw new Error("Stepper item parts must be used within <StepperItem>.");
17
17
  return context;
18
18
  }
19
+ function stepPartId(baseId, step, part) {
20
+ return `${baseId}-step-${step}-${part}`;
21
+ }
22
+ function useRegisteredId(id, rendered = true) {
23
+ const { registerId } = useStepper();
24
+ useEffect(() => rendered ? registerId(id) : void 0, [
25
+ id,
26
+ rendered,
27
+ registerId
28
+ ]);
29
+ }
30
+ function renderedIdRefs(renderedIds, ids) {
31
+ const refs = ids.filter((id) => renderedIds.has(id));
32
+ return refs.length > 0 ? refs.join(" ") : void 0;
33
+ }
19
34
  function stateForStep(value, step) {
20
35
  if (value === null) return "neutral";
21
36
  if (step < value) return "complete";
22
37
  if (step === value) return "current";
23
38
  return "upcoming";
24
39
  }
25
- function indicatorStateLabel(state, invalid) {
40
+ function statusStateLabel(state, invalid) {
26
41
  if (invalid) return "invalid";
27
42
  if (state === "neutral") return null;
28
43
  return state;
29
44
  }
45
+ function defaultStatusLabel({ step, state, invalid, disabled }) {
46
+ const stateLabel = statusStateLabel(state, invalid);
47
+ return [
48
+ `Step ${step + 1}`,
49
+ stateLabel,
50
+ disabled ? "disabled" : null
51
+ ].filter(Boolean).join(", ");
52
+ }
30
53
  function defaultIndicatorContent(step, state, invalid) {
31
54
  if (invalid) return /* @__PURE__ */ jsx(CircleAlertIcon, { className: "size-4" });
32
55
  if (state === "complete") return /* @__PURE__ */ jsx(CheckIcon, { className: "size-4" });
33
56
  return step + 1;
34
57
  }
35
58
  function itemLayout(orientation, responsive) {
36
- if (orientation === "vertical") return "grid min-w-0 grid-cols-[2rem_minmax(0,1fr)]";
37
- return cn("relative grid min-w-0 flex-1 grid-cols-1 justify-items-center", responsive && "@max-md/stepper:w-full @max-md/stepper:flex-none @max-md/stepper:grid-cols-[2rem_minmax(0,1fr)] @max-md/stepper:justify-items-stretch");
59
+ if (orientation === "vertical") return "grid min-w-0 grid-cols-[var(--cui-stepper-indicator-size)_minmax(0,1fr)]";
60
+ return cn("relative grid min-w-0 flex-1 grid-cols-1 justify-items-center", responsive && "@max-md/stepper:w-full @max-md/stepper:flex-none @max-md/stepper:grid-cols-[var(--cui-stepper-indicator-size)_minmax(0,1fr)] @max-md/stepper:justify-items-stretch");
38
61
  }
39
62
  function partLayout(orientation, responsive, part) {
40
63
  const vertical = {
@@ -56,9 +79,19 @@ function partLayout(orientation, responsive, part) {
56
79
  }[part];
57
80
  return cn(horizontal, narrow);
58
81
  }
59
- function Stepper({ value, defaultValue = null, onValueChange, orientation = "horizontal", contentMode = "current", responsive = true, id, className, children, ...props }) {
82
+ function Stepper({ value, defaultValue = null, onValueChange, orientation = "horizontal", contentMode = "current", responsive = true, getStatusLabel = defaultStatusLabel, id, className, children, ...props }) {
60
83
  const generatedId = useId();
61
84
  const [internalValue, setInternalValue] = useState(defaultValue);
85
+ const [renderedIds, setRenderedIds] = useState(() => /* @__PURE__ */ new Set());
86
+ const [registerId] = useState(() => (renderedId) => {
87
+ setRenderedIds((current) => current.has(renderedId) ? current : new Set(current).add(renderedId));
88
+ return () => setRenderedIds((current) => {
89
+ if (!current.has(renderedId)) return current;
90
+ const next = new Set(current);
91
+ next.delete(renderedId);
92
+ return next;
93
+ });
94
+ });
62
95
  const controlled = value !== void 0;
63
96
  const currentValue = value !== void 0 ? value : internalValue;
64
97
  const selectStep = (step) => {
@@ -72,7 +105,10 @@ function Stepper({ value, defaultValue = null, onValueChange, orientation = "hor
72
105
  contentMode,
73
106
  responsive,
74
107
  baseId: id ?? generatedId,
75
- selectStep
108
+ selectStep,
109
+ getStatusLabel,
110
+ renderedIds,
111
+ registerId
76
112
  },
77
113
  children: /* @__PURE__ */ jsx("div", {
78
114
  ...props,
@@ -107,8 +143,10 @@ function StepperItem({ step, disabled = false, invalid = false, className, child
107
143
  state,
108
144
  disabled,
109
145
  invalid,
110
- titleId: `${baseId}-step-${step}-title`,
111
- contentId: `${baseId}-step-${step}-content`
146
+ titleId: stepPartId(baseId, step, "title"),
147
+ descriptionId: stepPartId(baseId, step, "description"),
148
+ statusId: stepPartId(baseId, step, "status"),
149
+ contentId: stepPartId(baseId, step, "content")
112
150
  };
113
151
  return /* @__PURE__ */ jsx(StepperItemContext.Provider, {
114
152
  value: contextValue,
@@ -128,8 +166,8 @@ function StepperItem({ step, disabled = false, invalid = false, className, child
128
166
  });
129
167
  }
130
168
  function StepperTrigger({ disabled: disabledProp, className, onClick, type = "button", ...props }) {
131
- const { orientation, responsive, selectStep } = useStepper();
132
- const { step, state, disabled, invalid, contentId } = useStepperItem();
169
+ const { orientation, responsive, selectStep, renderedIds } = useStepper();
170
+ const { step, state, disabled, invalid, titleId, descriptionId, statusId, contentId } = useStepperItem();
133
171
  const isDisabled = disabled || Boolean(disabledProp);
134
172
  return /* @__PURE__ */ jsx("button", {
135
173
  ...props,
@@ -140,8 +178,11 @@ function StepperTrigger({ disabled: disabledProp, className, onClick, type = "bu
140
178
  "data-invalid": invalid ? "true" : void 0,
141
179
  type,
142
180
  disabled: isDisabled,
143
- "aria-controls": contentId,
144
- className: cn("group/stepper-trigger col-span-full row-span-3 row-start-1 grid w-full min-w-0 grid-cols-1 justify-items-center disabled:pointer-events-none disabled:cursor-not-allowed", orientation === "vertical" && "row-span-2 grid-cols-[2rem_minmax(0,1fr)] justify-items-stretch", orientation === "horizontal" && responsive && "@max-md/stepper:row-span-2 @max-md/stepper:grid-cols-[2rem_minmax(0,1fr)] @max-md/stepper:justify-items-stretch", className),
181
+ "aria-current": state === "current" ? "step" : void 0,
182
+ "aria-labelledby": renderedIdRefs(renderedIds, [titleId]),
183
+ "aria-describedby": renderedIdRefs(renderedIds, [statusId, descriptionId]),
184
+ "aria-controls": renderedIdRefs(renderedIds, [contentId]),
185
+ className: cn("group/stepper-trigger col-span-full row-span-3 row-start-1 grid w-full min-w-0 grid-cols-1 justify-items-center disabled:pointer-events-none disabled:cursor-not-allowed", orientation === "vertical" && "row-span-2 grid-cols-[var(--cui-stepper-indicator-size)_minmax(0,1fr)] justify-items-stretch", orientation === "horizontal" && responsive && "@max-md/stepper:row-span-2 @max-md/stepper:grid-cols-[var(--cui-stepper-indicator-size)_minmax(0,1fr)] @max-md/stepper:justify-items-stretch", className),
145
186
  onClick: (event) => {
146
187
  onClick?.(event);
147
188
  if (!event.defaultPrevented && !isDisabled) selectStep(step);
@@ -149,14 +190,9 @@ function StepperTrigger({ disabled: disabledProp, className, onClick, type = "bu
149
190
  });
150
191
  }
151
192
  function StepperIndicator({ className, children, ...props }) {
152
- const { orientation, responsive } = useStepper();
153
- const { step, state, disabled, invalid } = useStepperItem();
154
- const stateLabel = indicatorStateLabel(state, invalid);
155
- const status = [
156
- `Step ${step + 1}`,
157
- stateLabel,
158
- disabled ? "disabled" : null
159
- ].filter(Boolean).join(", ");
193
+ const { orientation, responsive, getStatusLabel } = useStepper();
194
+ const { step, state, disabled, invalid, statusId } = useStepperItem();
195
+ useRegisteredId(statusId);
160
196
  const indicatorContent = children ?? defaultIndicatorContent(step, state, invalid);
161
197
  return /* @__PURE__ */ jsxs("span", {
162
198
  ...props,
@@ -170,8 +206,14 @@ function StepperIndicator({ className, children, ...props }) {
170
206
  "aria-hidden": "true",
171
207
  children: indicatorContent
172
208
  }), /* @__PURE__ */ jsx("span", {
209
+ id: statusId,
173
210
  className: "sr-only",
174
- children: status
211
+ children: getStatusLabel({
212
+ step,
213
+ state,
214
+ invalid,
215
+ disabled
216
+ })
175
217
  })]
176
218
  });
177
219
  }
@@ -186,12 +228,13 @@ function StepperSeparator({ className, ...props }) {
186
228
  "data-state": state,
187
229
  "data-invalid": invalid ? "true" : void 0,
188
230
  "aria-hidden": "true",
189
- className: cn("absolute", orientation === "horizontal" && "top-4 right-[calc(-50%+1rem)] left-[calc(50%+1rem)]", orientation === "vertical" && "top-8 bottom-0 left-4", orientation === "horizontal" && responsive && "@max-md/stepper:top-8 @max-md/stepper:right-auto @max-md/stepper:bottom-0 @max-md/stepper:left-4", className)
231
+ className: cn("absolute", orientation === "horizontal" && "top-[calc(var(--cui-stepper-indicator-size)/2)] start-[calc(50%_+_var(--cui-stepper-indicator-size)/2)] end-[calc(var(--cui-stepper-indicator-size)/2_-_50%)]", orientation === "vertical" && "top-(--cui-stepper-indicator-size) bottom-0 start-[calc(var(--cui-stepper-indicator-size)/2)]", orientation === "horizontal" && responsive && "@max-md/stepper:top-(--cui-stepper-indicator-size) @max-md/stepper:end-auto @max-md/stepper:bottom-0 @max-md/stepper:start-[calc(var(--cui-stepper-indicator-size)/2)]", className)
190
232
  });
191
233
  }
192
234
  function StepperTitle({ className, ...props }) {
193
235
  const { orientation, responsive } = useStepper();
194
236
  const { titleId } = useStepperItem();
237
+ useRegisteredId(titleId);
195
238
  return /* @__PURE__ */ jsx("span", {
196
239
  ...props,
197
240
  id: titleId,
@@ -203,8 +246,11 @@ function StepperTitle({ className, ...props }) {
203
246
  }
204
247
  function StepperDescription({ className, ...props }) {
205
248
  const { orientation, responsive } = useStepper();
249
+ const { descriptionId } = useStepperItem();
250
+ useRegisteredId(descriptionId);
206
251
  return /* @__PURE__ */ jsx("span", {
207
252
  ...props,
253
+ id: descriptionId,
208
254
  "data-control-ui": "stepper",
209
255
  "data-control-family": "stepper",
210
256
  "data-slot": "description",
@@ -215,16 +261,19 @@ function StepperContent({ step, keepMounted = true, className, ...props }) {
215
261
  const { value, contentMode, baseId } = useStepper();
216
262
  const active = value === step;
217
263
  const hidden = contentMode === "current" && !active;
218
- if (hidden && !keepMounted) return null;
264
+ const rendered = !hidden || keepMounted;
265
+ const contentId = stepPartId(baseId, step, "content");
266
+ useRegisteredId(contentId, rendered);
267
+ if (!rendered) return null;
219
268
  return /* @__PURE__ */ jsx("section", {
220
269
  ...props,
221
- id: `${baseId}-step-${step}-content`,
270
+ id: contentId,
222
271
  "data-control-ui": "stepper",
223
272
  "data-control-family": "stepper",
224
273
  "data-slot": "content",
225
274
  "data-state": active ? "active" : "inactive",
226
275
  hidden,
227
- "aria-labelledby": `${baseId}-step-${step}-title`,
276
+ "aria-labelledby": stepPartId(baseId, step, "title"),
228
277
  className
229
278
  });
230
279
  }
@@ -18,6 +18,7 @@ export type SwitchProps = Omit<{
18
18
  "aria-label"?: string;
19
19
  "aria-labelledby"?: string;
20
20
  "aria-describedby"?: string;
21
+ "aria-invalid"?: boolean | "true" | "false";
21
22
  }, "style"> & {
22
23
  style?: CSSProperties & SwitchKnobStyle;
23
24
  };
@@ -1,5 +1,5 @@
1
1
  import { TableOfContentsKnobStyle } from "../knob-contracts/table-of-contents-knobs.js";
2
- import { CSSProperties, ComponentProps, ReactNode } from "react";
2
+ import { CSSProperties, ComponentProps, ReactNode, RefObject } from "react";
3
3
  //#region src/ui/table-of-contents.d.ts
4
4
  export type TocItem = {
5
5
  href: string;
@@ -17,6 +17,6 @@ export type TableOfContentsProps = Omit<Omit<ComponentProps<"nav">, "children">,
17
17
  variant?: TableOfContentsVariant;
18
18
  indicator?: ReactNode;
19
19
  };
20
- export declare function useVisibleSections(ids: string[]): string[];
20
+ export declare function useVisibleSections(ids: string[], knobSourceRef: RefObject<HTMLElement | null>): string[];
21
21
  export declare function TableOfContents({ items, label, variant, indicator, className, style, ...props }: TableOfContentsProps): import("react").JSX.Element | null;
22
22
  //#endregion