@refraktor/core 0.0.1 → 0.0.3

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 (290) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/build/components/breadcrumbs/breadcrumbs.d.ts +4 -0
  3. package/build/components/breadcrumbs/breadcrumbs.d.ts.map +1 -0
  4. package/build/components/breadcrumbs/breadcrumbs.js +64 -0
  5. package/build/components/breadcrumbs/breadcrumbs.styles.d.ts +12 -0
  6. package/build/components/breadcrumbs/breadcrumbs.styles.d.ts.map +1 -0
  7. package/build/components/breadcrumbs/breadcrumbs.styles.js +43 -0
  8. package/build/components/breadcrumbs/breadcrumbs.test.d.ts +2 -0
  9. package/build/components/breadcrumbs/breadcrumbs.test.d.ts.map +1 -0
  10. package/build/components/breadcrumbs/breadcrumbs.test.js +72 -0
  11. package/build/components/breadcrumbs/breadcrumbs.types.d.ts +56 -0
  12. package/build/components/breadcrumbs/breadcrumbs.types.d.ts.map +1 -0
  13. package/build/components/breadcrumbs/breadcrumbs.types.js +1 -0
  14. package/build/components/breadcrumbs/breadcrumbs.utils.d.ts +10 -0
  15. package/build/components/breadcrumbs/breadcrumbs.utils.d.ts.map +1 -0
  16. package/build/components/breadcrumbs/breadcrumbs.utils.js +36 -0
  17. package/build/components/breadcrumbs/index.d.ts +3 -0
  18. package/build/components/breadcrumbs/index.d.ts.map +1 -0
  19. package/build/components/breadcrumbs/index.js +1 -0
  20. package/build/components/drawer/drawer-close/drawer-close.d.ts +4 -0
  21. package/build/components/drawer/drawer-close/drawer-close.d.ts.map +1 -0
  22. package/build/components/drawer/drawer-close/drawer-close.js +19 -0
  23. package/build/components/drawer/drawer-close/index.d.ts +2 -0
  24. package/build/components/drawer/drawer-close/index.d.ts.map +1 -0
  25. package/build/components/drawer/drawer-close/index.js +1 -0
  26. package/build/components/drawer/drawer-content/drawer-content.d.ts +4 -0
  27. package/build/components/drawer/drawer-content/drawer-content.d.ts.map +1 -0
  28. package/build/components/drawer/drawer-content/drawer-content.js +41 -0
  29. package/build/components/drawer/drawer-content/index.d.ts +2 -0
  30. package/build/components/drawer/drawer-content/index.d.ts.map +1 -0
  31. package/build/components/drawer/drawer-content/index.js +1 -0
  32. package/build/components/drawer/drawer-header/drawer-header.d.ts +4 -0
  33. package/build/components/drawer/drawer-header/drawer-header.d.ts.map +1 -0
  34. package/build/components/drawer/drawer-header/drawer-header.js +13 -0
  35. package/build/components/drawer/drawer-header/index.d.ts +2 -0
  36. package/build/components/drawer/drawer-header/index.d.ts.map +1 -0
  37. package/build/components/drawer/drawer-header/index.js +1 -0
  38. package/build/components/drawer/drawer-overlay/drawer-overlay.d.ts +4 -0
  39. package/build/components/drawer/drawer-overlay/drawer-overlay.d.ts.map +1 -0
  40. package/build/components/drawer/drawer-overlay/drawer-overlay.js +31 -0
  41. package/build/components/drawer/drawer-overlay/index.d.ts +2 -0
  42. package/build/components/drawer/drawer-overlay/index.d.ts.map +1 -0
  43. package/build/components/drawer/drawer-overlay/index.js +1 -0
  44. package/build/components/drawer/drawer-root/drawer-root.d.ts +4 -0
  45. package/build/components/drawer/drawer-root/drawer-root.d.ts.map +1 -0
  46. package/build/components/drawer/drawer-root/drawer-root.js +50 -0
  47. package/build/components/drawer/drawer-root/index.d.ts +2 -0
  48. package/build/components/drawer/drawer-root/index.d.ts.map +1 -0
  49. package/build/components/drawer/drawer-root/index.js +1 -0
  50. package/build/components/drawer/drawer.context.d.ts +23 -0
  51. package/build/components/drawer/drawer.context.d.ts.map +1 -0
  52. package/build/components/drawer/drawer.context.js +2 -0
  53. package/build/components/drawer/drawer.d.ts +4 -0
  54. package/build/components/drawer/drawer.d.ts.map +1 -0
  55. package/build/components/drawer/drawer.js +19 -0
  56. package/build/components/drawer/drawer.styles.d.ts +6 -0
  57. package/build/components/drawer/drawer.styles.d.ts.map +1 -0
  58. package/build/components/drawer/drawer.styles.js +21 -0
  59. package/build/components/drawer/drawer.test.d.ts +2 -0
  60. package/build/components/drawer/drawer.test.d.ts.map +1 -0
  61. package/build/components/drawer/drawer.test.js +70 -0
  62. package/build/components/drawer/drawer.types.d.ts +116 -0
  63. package/build/components/drawer/drawer.types.d.ts.map +1 -0
  64. package/build/components/drawer/drawer.types.js +1 -0
  65. package/build/components/drawer/index.d.ts +8 -0
  66. package/build/components/drawer/index.d.ts.map +1 -0
  67. package/build/components/drawer/index.js +6 -0
  68. package/build/components/drawer/use-drawer.d.ts +17 -0
  69. package/build/components/drawer/use-drawer.d.ts.map +1 -0
  70. package/build/components/drawer/use-drawer.js +61 -0
  71. package/build/components/index.d.ts +4 -0
  72. package/build/components/index.d.ts.map +1 -1
  73. package/build/components/index.js +4 -0
  74. package/build/components/menu/menu-dropdown/menu-dropdown.d.ts.map +1 -1
  75. package/build/components/menu/menu-dropdown/menu-dropdown.js +3 -2
  76. package/build/components/menu/menu-sub-dropdown/menu-sub-dropdown.d.ts.map +1 -1
  77. package/build/components/menu/menu-sub-dropdown/menu-sub-dropdown.js +2 -1
  78. package/build/components/menu/use-menu.d.ts.map +1 -1
  79. package/build/components/menu/use-menu.js +2 -1
  80. package/build/components/modal/modal-close/modal-close.d.ts.map +1 -1
  81. package/build/components/modal/modal-close/modal-close.js +1 -1
  82. package/build/components/modal/modal-content/modal-content.d.ts.map +1 -1
  83. package/build/components/modal/modal-content/modal-content.js +1 -1
  84. package/build/components/modal/modal-header/modal-header.d.ts.map +1 -1
  85. package/build/components/modal/modal-header/modal-header.js +1 -1
  86. package/build/components/modal/modal-overlay/modal-overlay.d.ts.map +1 -1
  87. package/build/components/modal/modal-overlay/modal-overlay.js +10 -2
  88. package/build/components/modal/modal.test.js +16 -0
  89. package/build/components/modal/modal.types.d.ts +4 -0
  90. package/build/components/modal/modal.types.d.ts.map +1 -1
  91. package/build/components/number-input/number-input.d.ts.map +1 -1
  92. package/build/components/number-input/number-input.js +22 -15
  93. package/build/components/number-input/number-input.test.d.ts +2 -0
  94. package/build/components/number-input/number-input.test.d.ts.map +1 -0
  95. package/build/components/number-input/number-input.test.js +14 -0
  96. package/build/components/number-input/number-input.types.d.ts +2 -2
  97. package/build/components/number-input/number-input.types.d.ts.map +1 -1
  98. package/build/components/popover/popover-dropdown/popover-dropdown.d.ts.map +1 -1
  99. package/build/components/popover/popover-dropdown/popover-dropdown.js +2 -1
  100. package/build/components/popover/use-popover.d.ts.map +1 -1
  101. package/build/components/popover/use-popover.js +2 -1
  102. package/build/components/portal/portal.js +1 -1
  103. package/build/components/segmented-control/index.d.ts +3 -0
  104. package/build/components/segmented-control/index.d.ts.map +1 -0
  105. package/build/components/segmented-control/index.js +1 -0
  106. package/build/components/segmented-control/segmented-control.d.ts +4 -0
  107. package/build/components/segmented-control/segmented-control.d.ts.map +1 -0
  108. package/build/components/segmented-control/segmented-control.js +113 -0
  109. package/build/components/segmented-control/segmented-control.styles.d.ts +9 -0
  110. package/build/components/segmented-control/segmented-control.styles.d.ts.map +1 -0
  111. package/build/components/segmented-control/segmented-control.styles.js +28 -0
  112. package/build/components/segmented-control/segmented-control.test.d.ts +2 -0
  113. package/build/components/segmented-control/segmented-control.test.d.ts.map +1 -0
  114. package/build/components/segmented-control/segmented-control.test.js +81 -0
  115. package/build/components/segmented-control/segmented-control.types.d.ts +49 -0
  116. package/build/components/segmented-control/segmented-control.types.d.ts.map +1 -0
  117. package/build/components/segmented-control/segmented-control.types.js +1 -0
  118. package/build/components/select/select-dropdown/select-dropdown.d.ts.map +1 -1
  119. package/build/components/select/select-dropdown/select-dropdown.js +3 -2
  120. package/build/components/select/select-item/select-item.d.ts.map +1 -1
  121. package/build/components/select/select-item/select-item.js +1 -1
  122. package/build/components/select/select-root/select-root.d.ts.map +1 -1
  123. package/build/components/select/select-root/select-root.js +36 -6
  124. package/build/components/select/select-trigger/select-trigger.d.ts.map +1 -1
  125. package/build/components/select/select-trigger/select-trigger.js +1 -1
  126. package/build/components/select/select.context.d.ts +2 -0
  127. package/build/components/select/select.context.d.ts.map +1 -1
  128. package/build/components/select/select.test.js +17 -0
  129. package/build/components/select/select.types.d.ts +10 -0
  130. package/build/components/select/select.types.d.ts.map +1 -1
  131. package/build/components/select/use-select.d.ts.map +1 -1
  132. package/build/components/select/use-select.js +2 -1
  133. package/build/components/split-pane/index.d.ts +3 -0
  134. package/build/components/split-pane/index.d.ts.map +1 -0
  135. package/build/components/split-pane/index.js +1 -0
  136. package/build/components/split-pane/split-pane.d.ts +4 -0
  137. package/build/components/split-pane/split-pane.d.ts.map +1 -0
  138. package/build/components/split-pane/split-pane.js +201 -0
  139. package/build/components/split-pane/split-pane.styles.d.ts +3 -0
  140. package/build/components/split-pane/split-pane.styles.d.ts.map +1 -0
  141. package/build/components/split-pane/split-pane.styles.js +8 -0
  142. package/build/components/split-pane/split-pane.test.d.ts +2 -0
  143. package/build/components/split-pane/split-pane.test.d.ts.map +1 -0
  144. package/build/components/split-pane/split-pane.test.js +105 -0
  145. package/build/components/split-pane/split-pane.types.d.ts +51 -0
  146. package/build/components/split-pane/split-pane.types.d.ts.map +1 -0
  147. package/build/components/split-pane/split-pane.types.js +1 -0
  148. package/build/components/switch/switch.js +1 -1
  149. package/build/components/table/index.d.ts +9 -0
  150. package/build/components/table/index.d.ts.map +1 -0
  151. package/build/components/table/index.js +7 -0
  152. package/build/components/table/table-body/index.d.ts +2 -0
  153. package/build/components/table/table-body/index.d.ts.map +1 -0
  154. package/build/components/table/table-body/index.js +1 -0
  155. package/build/components/table/table-body/table-body.d.ts +4 -0
  156. package/build/components/table/table-body/table-body.d.ts.map +1 -0
  157. package/build/components/table/table-body/table-body.js +17 -0
  158. package/build/components/table/table-caption/index.d.ts +2 -0
  159. package/build/components/table/table-caption/index.d.ts.map +1 -0
  160. package/build/components/table/table-caption/index.js +1 -0
  161. package/build/components/table/table-caption/table-caption.d.ts +4 -0
  162. package/build/components/table/table-caption/table-caption.d.ts.map +1 -0
  163. package/build/components/table/table-caption/table-caption.js +13 -0
  164. package/build/components/table/table-cell/index.d.ts +2 -0
  165. package/build/components/table/table-cell/index.d.ts.map +1 -0
  166. package/build/components/table/table-cell/index.js +1 -0
  167. package/build/components/table/table-cell/table-cell.d.ts +4 -0
  168. package/build/components/table/table-cell/table-cell.d.ts.map +1 -0
  169. package/build/components/table/table-cell/table-cell.js +13 -0
  170. package/build/components/table/table-head/index.d.ts +2 -0
  171. package/build/components/table/table-head/index.d.ts.map +1 -0
  172. package/build/components/table/table-head/index.js +1 -0
  173. package/build/components/table/table-head/table-head.d.ts +4 -0
  174. package/build/components/table/table-head/table-head.d.ts.map +1 -0
  175. package/build/components/table/table-head/table-head.js +11 -0
  176. package/build/components/table/table-header-cell/index.d.ts +2 -0
  177. package/build/components/table/table-header-cell/index.d.ts.map +1 -0
  178. package/build/components/table/table-header-cell/index.js +1 -0
  179. package/build/components/table/table-header-cell/table-header-cell.d.ts +4 -0
  180. package/build/components/table/table-header-cell/table-header-cell.d.ts.map +1 -0
  181. package/build/components/table/table-header-cell/table-header-cell.js +13 -0
  182. package/build/components/table/table-row/index.d.ts +2 -0
  183. package/build/components/table/table-row/index.d.ts.map +1 -0
  184. package/build/components/table/table-row/index.js +1 -0
  185. package/build/components/table/table-row/table-row.d.ts +4 -0
  186. package/build/components/table/table-row/table-row.d.ts.map +1 -0
  187. package/build/components/table/table-row/table-row.js +11 -0
  188. package/build/components/table/table.context.d.ts +16 -0
  189. package/build/components/table/table.context.d.ts.map +1 -0
  190. package/build/components/table/table.context.js +2 -0
  191. package/build/components/table/table.d.ts +4 -0
  192. package/build/components/table/table.d.ts.map +1 -0
  193. package/build/components/table/table.js +46 -0
  194. package/build/components/table/table.styles.d.ts +16 -0
  195. package/build/components/table/table.styles.d.ts.map +1 -0
  196. package/build/components/table/table.styles.js +39 -0
  197. package/build/components/table/table.test.d.ts +2 -0
  198. package/build/components/table/table.test.d.ts.map +1 -0
  199. package/build/components/table/table.test.js +59 -0
  200. package/build/components/table/table.types.d.ts +113 -0
  201. package/build/components/table/table.types.d.ts.map +1 -0
  202. package/build/components/table/table.types.js +1 -0
  203. package/build/components/tabs/tabs-tab/tabs-tab.d.ts.map +1 -1
  204. package/build/components/tabs/tabs-tab/tabs-tab.js +0 -6
  205. package/build/components/tooltip/tooltip.d.ts.map +1 -1
  206. package/build/components/tooltip/tooltip.js +7 -3
  207. package/build/components/tooltip/use-tooltip.d.ts.map +1 -1
  208. package/build/components/tooltip/use-tooltip.js +2 -1
  209. package/build/components/transition/transition.d.ts.map +1 -1
  210. package/build/components/transition/transition.js +16 -12
  211. package/build/style.css +1 -1
  212. package/package.json +2 -2
  213. package/src/components/breadcrumbs/breadcrumbs.styles.ts +55 -0
  214. package/src/components/breadcrumbs/breadcrumbs.test.tsx +136 -0
  215. package/src/components/breadcrumbs/breadcrumbs.tsx +199 -0
  216. package/src/components/breadcrumbs/breadcrumbs.types.ts +78 -0
  217. package/src/components/breadcrumbs/breadcrumbs.utils.ts +70 -0
  218. package/src/components/breadcrumbs/index.ts +6 -0
  219. package/src/components/drawer/drawer-close/drawer-close.tsx +43 -0
  220. package/src/components/drawer/drawer-close/index.ts +1 -0
  221. package/src/components/drawer/drawer-content/drawer-content.tsx +98 -0
  222. package/src/components/drawer/drawer-content/index.ts +1 -0
  223. package/src/components/drawer/drawer-header/drawer-header.tsx +40 -0
  224. package/src/components/drawer/drawer-header/index.ts +1 -0
  225. package/src/components/drawer/drawer-overlay/drawer-overlay.tsx +86 -0
  226. package/src/components/drawer/drawer-overlay/index.ts +1 -0
  227. package/src/components/drawer/drawer-root/drawer-root.tsx +94 -0
  228. package/src/components/drawer/drawer-root/index.ts +1 -0
  229. package/src/components/drawer/drawer.context.ts +25 -0
  230. package/src/components/drawer/drawer.styles.ts +32 -0
  231. package/src/components/drawer/drawer.test.tsx +166 -0
  232. package/src/components/drawer/drawer.tsx +30 -0
  233. package/src/components/drawer/drawer.types.ts +158 -0
  234. package/src/components/drawer/index.ts +16 -0
  235. package/src/components/drawer/use-drawer.ts +101 -0
  236. package/src/components/index.ts +10 -6
  237. package/src/components/menu/menu-dropdown/menu-dropdown.tsx +4 -3
  238. package/src/components/menu/menu-sub-dropdown/menu-sub-dropdown.tsx +2 -0
  239. package/src/components/menu/use-menu.ts +2 -1
  240. package/src/components/modal/modal-close/modal-close.tsx +1 -5
  241. package/src/components/modal/modal-content/modal-content.tsx +3 -4
  242. package/src/components/modal/modal-header/modal-header.tsx +4 -2
  243. package/src/components/modal/modal-overlay/modal-overlay.tsx +66 -44
  244. package/src/components/modal/modal.test.tsx +124 -88
  245. package/src/components/modal/modal.types.ts +6 -0
  246. package/src/components/modal/use-modal.ts +101 -101
  247. package/src/components/number-input/number-input.test.tsx +22 -0
  248. package/src/components/number-input/number-input.tsx +79 -51
  249. package/src/components/number-input/number-input.types.ts +8 -8
  250. package/src/components/popover/popover-dropdown/popover-dropdown.tsx +2 -0
  251. package/src/components/popover/use-popover.ts +2 -1
  252. package/src/components/portal/portal.tsx +1 -1
  253. package/src/components/segmented-control/index.ts +6 -0
  254. package/src/components/segmented-control/segmented-control.styles.ts +37 -0
  255. package/src/components/segmented-control/segmented-control.test.tsx +152 -0
  256. package/src/components/segmented-control/segmented-control.tsx +245 -0
  257. package/src/components/segmented-control/segmented-control.types.ts +75 -0
  258. package/src/components/select/select-dropdown/select-dropdown.tsx +4 -3
  259. package/src/components/select/select-item/select-item.tsx +1 -2
  260. package/src/components/select/select-root/select-root.tsx +87 -11
  261. package/src/components/select/select-trigger/select-trigger.tsx +2 -0
  262. package/src/components/select/select.context.ts +2 -0
  263. package/src/components/select/select.test.tsx +35 -0
  264. package/src/components/select/select.types.ts +15 -0
  265. package/src/components/select/use-select.ts +2 -1
  266. package/src/components/switch/switch.tsx +1 -1
  267. package/src/components/table/index.ts +24 -0
  268. package/src/components/table/table-body/index.ts +1 -0
  269. package/src/components/table/table-body/table-body.tsx +37 -0
  270. package/src/components/table/table-caption/index.ts +1 -0
  271. package/src/components/table/table-caption/table-caption.tsx +32 -0
  272. package/src/components/table/table-cell/index.ts +1 -0
  273. package/src/components/table/table-cell/table-cell.tsx +33 -0
  274. package/src/components/table/table-head/index.ts +1 -0
  275. package/src/components/table/table-head/table-head.tsx +29 -0
  276. package/src/components/table/table-header-cell/index.ts +1 -0
  277. package/src/components/table/table-header-cell/table-header-cell.tsx +33 -0
  278. package/src/components/table/table-row/index.ts +1 -0
  279. package/src/components/table/table-row/table-row.tsx +30 -0
  280. package/src/components/table/table.context.ts +18 -0
  281. package/src/components/table/table.styles.ts +62 -0
  282. package/src/components/table/table.test.tsx +145 -0
  283. package/src/components/table/table.tsx +91 -0
  284. package/src/components/table/table.types.ts +145 -0
  285. package/src/components/tabs/tabs-tab/tabs-tab.tsx +0 -8
  286. package/src/components/tooltip/tooltip.tsx +7 -1
  287. package/src/components/tooltip/use-tooltip.ts +2 -1
  288. package/src/components/transition/transition.tsx +18 -14
  289. package/src/style.css +0 -1
  290. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,22 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { render, screen } from "../../vitest";
3
+ import NumberInput from "./number-input";
4
+
5
+ describe("@refraktor/core/NumberInput", () => {
6
+ it("supports input wrapper props", async () => {
7
+ await render(
8
+ <NumberInput
9
+ label="Quantity"
10
+ description="Enter desired amount"
11
+ error="Quantity is required"
12
+ />
13
+ );
14
+
15
+ const input = screen.getByLabelText("Quantity");
16
+
17
+ expect(input).toHaveAttribute("type", "number");
18
+ expect(input).toHaveAttribute("aria-invalid", "true");
19
+ expect(screen.getByText("Enter desired amount")).toBeInTheDocument();
20
+ expect(screen.getByText("Quantity is required")).toBeInTheDocument();
21
+ });
22
+ });
@@ -69,6 +69,11 @@ const NumberInput = factory<NumberInputFactoryPayload>((_props, ref) => {
69
69
  const { cx } = useTheme();
70
70
  const {
71
71
  id,
72
+ label,
73
+ description,
74
+ error,
75
+ required,
76
+ withAsterisk,
72
77
  value,
73
78
  defaultValue,
74
79
  size,
@@ -134,8 +139,8 @@ const NumberInput = factory<NumberInputFactoryPayload>((_props, ref) => {
134
139
  };
135
140
 
136
141
  const stopHold = () => {
137
- if (holdTimer.current) clearTimeout(holdTimer.current);
138
- if (repeatTimer.current) clearInterval(repeatTimer.current);
142
+ if (holdTimer.current) clearInterval(holdTimer.current);
143
+ if (repeatTimer.current) clearTimeout(repeatTimer.current);
139
144
  holdTimer.current = null;
140
145
  repeatTimer.current = null;
141
146
  };
@@ -292,56 +297,79 @@ const NumberInput = factory<NumberInputFactoryPayload>((_props, ref) => {
292
297
  );
293
298
  }, [controlsPosition, canIncrement, canDecrement, classes, controlSizing]);
294
299
 
300
+ const hasWrapper = label || description || error;
301
+
302
+ const field = (
303
+ <InputField
304
+ ref={ref}
305
+ id={_id}
306
+ type="number"
307
+ required={required}
308
+ error={!!error}
309
+ disabled={disabled}
310
+ size={size}
311
+ min={min}
312
+ max={max}
313
+ step={step}
314
+ value={_value}
315
+ onChange={handleChange}
316
+ onBlur={handleBlur}
317
+ onKeyDown={handleKeyDown}
318
+ onWheel={handleWheel}
319
+ leftSection={controlsPosition === "left" ? controls : undefined}
320
+ rightSection={controlsPosition === "right" ? controls : undefined}
321
+ className={className}
322
+ classNames={{
323
+ root: cx(
324
+ "px-0 gap-0 items-stretch overflow-hidden",
325
+ controlsPosition === "none"
326
+ ? "px-2"
327
+ : controlsPosition === "left"
328
+ ? "pr-2"
329
+ : "pl-2",
330
+ classes.root
331
+ ),
332
+ leftSection: cx(
333
+ "h-full self-stretch",
334
+ controlsPosition === "left" && controlSizing.leftSpacing,
335
+ classes.leftSection
336
+ ),
337
+ rightSection: cx(
338
+ "h-full self-stretch",
339
+ controlsPosition === "right" && controlSizing.rightSpacing,
340
+ classes.rightSection
341
+ ),
342
+ ...(classes as any)
343
+ }}
344
+ aria-describedby={
345
+ error
346
+ ? `${_id}-error`
347
+ : description
348
+ ? `${_id}-description`
349
+ : undefined
350
+ }
351
+ aria-disabled={disabled}
352
+ aria-valuemin={min}
353
+ aria-valuemax={max}
354
+ aria-valuenow={Number(_value)}
355
+ {...props}
356
+ />
357
+ );
358
+
359
+ if (!hasWrapper) {
360
+ return field;
361
+ }
362
+
295
363
  return (
296
- <InputWrapper>
297
- <InputField
298
- ref={ref}
299
- type="number"
300
- disabled={disabled}
301
- size={size}
302
- min={min}
303
- max={max}
304
- step={step}
305
- value={_value}
306
- onChange={handleChange}
307
- onBlur={handleBlur}
308
- onKeyDown={handleKeyDown}
309
- onWheel={handleWheel}
310
- leftSection={controlsPosition === "left" ? controls : undefined}
311
- rightSection={
312
- controlsPosition === "right" ? controls : undefined
313
- }
314
- className={className}
315
- classNames={{
316
- root: cx(
317
- "px-0 gap-0 items-stretch overflow-hidden",
318
- controlsPosition === "none"
319
- ? "px-2"
320
- : controlsPosition === "left"
321
- ? "pr-2"
322
- : "pl-2",
323
- classes.root
324
- ),
325
- leftSection: cx(
326
- "h-full self-stretch",
327
- controlsPosition === "left" &&
328
- controlSizing.leftSpacing,
329
- classes.leftSection
330
- ),
331
- rightSection: cx(
332
- "h-full self-stretch",
333
- controlsPosition === "right" &&
334
- controlSizing.rightSpacing,
335
- classes.rightSection
336
- ),
337
- ...(classes as any)
338
- }}
339
- aria-disabled={disabled}
340
- aria-valuemin={min}
341
- aria-valuemax={max}
342
- aria-valuenow={Number(_value)}
343
- {...props}
344
- />
364
+ <InputWrapper
365
+ label={label}
366
+ description={description}
367
+ error={error}
368
+ required={required}
369
+ withAsterisk={withAsterisk}
370
+ inputId={_id}
371
+ >
372
+ {field}
345
373
  </InputWrapper>
346
374
  );
347
375
  });
@@ -1,9 +1,9 @@
1
- import {
2
- createClassNamesConfig,
3
- createComponentConfig,
4
- FactoryPayload
5
- } from "../../utils";
6
- import { InputFieldClassNames, InputFieldProps } from "../input";
1
+ import {
2
+ createClassNamesConfig,
3
+ createComponentConfig,
4
+ FactoryPayload
5
+ } from "../../utils";
6
+ import { InputFieldClassNames, InputProps } from "../input";
7
7
 
8
8
  export type NumberInputControlsPosition = "left" | "right" | "none";
9
9
 
@@ -43,8 +43,8 @@ export interface _NumberInputProps {
43
43
  classNames?: NumberInputClassNames;
44
44
  }
45
45
 
46
- export type NumberInputProps = _NumberInputProps &
47
- Omit<InputFieldProps, "onChange" | "type" | keyof _NumberInputProps>;
46
+ export type NumberInputProps = _NumberInputProps &
47
+ Omit<InputProps, "onChange" | "type" | keyof _NumberInputProps>;
48
48
 
49
49
  export interface NumberInputFactoryPayload extends FactoryPayload {
50
50
  props: NumberInputProps;
@@ -26,6 +26,7 @@ const PopoverDropdown = factory<PopoverDropdownFactoryPayload>(
26
26
  transition="fade"
27
27
  duration={200}
28
28
  mounted={popover.opened}
29
+ style={{ position: "relative", zIndex: 1000 }}
29
30
  {...transitionProps}
30
31
  >
31
32
  <div
@@ -42,6 +43,7 @@ const PopoverDropdown = factory<PopoverDropdownFactoryPayload>(
42
43
  aria-modal="true"
43
44
  style={{
44
45
  ...popover.floatingStyles,
46
+ zIndex: 1000,
45
47
  ...style
46
48
  }}
47
49
  className={cx(
@@ -142,7 +142,8 @@ export function usePopover(options: UsePopoverProps = {}): UsePopoverReturn {
142
142
  open: isOpen,
143
143
  onOpenChange: setIsOpen,
144
144
  middleware: middleware,
145
- whileElementsMounted: autoUpdate
145
+ whileElementsMounted: autoUpdate,
146
+ strategy: "fixed"
146
147
  });
147
148
 
148
149
  const click = useClick(floating.context, {
@@ -98,7 +98,7 @@ const Portal = factory<PortalFactoryPayload>((_props, ref) => {
98
98
  return createPortal(<>{children}</>, nodeRef.current);
99
99
  });
100
100
 
101
- Portal.displayName = "@velra/core/Portal";
101
+ Portal.displayName = "@refraktor/core/Portal";
102
102
  Portal.configure = createComponentConfig<PortalProps>();
103
103
 
104
104
  export default Portal;
@@ -0,0 +1,6 @@
1
+ export { default as SegmentedControl } from "./segmented-control";
2
+ export type {
3
+ SegmentedControlProps,
4
+ SegmentedControlClassNames,
5
+ SegmentedControlItem
6
+ } from "./segmented-control.types";
@@ -0,0 +1,37 @@
1
+ import { RefraktorSize } from "../../theme";
2
+
3
+ type SegmentedControlSizeStyles = {
4
+ root: string;
5
+ control: string;
6
+ label: string;
7
+ };
8
+
9
+ const sizes: Record<RefraktorSize, SegmentedControlSizeStyles> = {
10
+ xs: {
11
+ root: "p-0.5 gap-0.5",
12
+ control: "h-6 px-2 min-w-7",
13
+ label: "text-[10px]"
14
+ },
15
+ sm: {
16
+ root: "p-0.5 gap-0.5",
17
+ control: "h-7 px-2.5 min-w-8",
18
+ label: "text-xs"
19
+ },
20
+ md: {
21
+ root: "p-1 gap-1",
22
+ control: "h-8 px-3 min-w-9",
23
+ label: "text-sm"
24
+ },
25
+ lg: {
26
+ root: "p-1 gap-1",
27
+ control: "h-9 px-3.5 min-w-10",
28
+ label: "text-base"
29
+ },
30
+ xl: {
31
+ root: "p-1.5 gap-1.5",
32
+ control: "h-10 px-4 min-w-11",
33
+ label: "text-lg"
34
+ }
35
+ };
36
+
37
+ export const getSize = (size: RefraktorSize = "md") => sizes[size];
@@ -0,0 +1,152 @@
1
+ import { createRef, useState } from "react";
2
+ import { describe, expect, it, vi } from "vitest";
3
+ import { render, screen, userEvent } from "../../vitest";
4
+ import SegmentedControl from "./segmented-control";
5
+
6
+ const data = [
7
+ { value: "react", label: "React" },
8
+ { value: "vue", label: "Vue" },
9
+ { value: "svelte", label: "Svelte" }
10
+ ];
11
+
12
+ describe("@refraktor/core/SegmentedControl", () => {
13
+ it("renders options and selected value", async () => {
14
+ await render(<SegmentedControl data={data} defaultValue="react" />);
15
+
16
+ expect(screen.getByRole("radiogroup")).toBeInTheDocument();
17
+ expect(screen.getByRole("radio", { name: "React" })).toHaveAttribute(
18
+ "aria-checked",
19
+ "true"
20
+ );
21
+ expect(screen.getByRole("radio", { name: "Vue" })).toHaveAttribute(
22
+ "aria-checked",
23
+ "false"
24
+ );
25
+ });
26
+
27
+ it("forwards ref correctly", async () => {
28
+ const ref = createRef<HTMLDivElement>();
29
+
30
+ await render(<SegmentedControl ref={ref} data={data} />);
31
+
32
+ expect(ref.current).toBeInstanceOf(HTMLDivElement);
33
+ expect(ref.current?.tagName).toBe("DIV");
34
+ });
35
+
36
+ it("handles click change events", async () => {
37
+ const user = userEvent.setup();
38
+ const onChange = vi.fn();
39
+
40
+ await render(
41
+ <SegmentedControl
42
+ data={data}
43
+ defaultValue="react"
44
+ onChange={onChange}
45
+ />
46
+ );
47
+
48
+ await user.click(screen.getByRole("radio", { name: "Vue" }));
49
+
50
+ expect(onChange).toHaveBeenCalledWith("vue");
51
+ expect(screen.getByRole("radio", { name: "Vue" })).toHaveAttribute(
52
+ "aria-checked",
53
+ "true"
54
+ );
55
+ });
56
+
57
+ it("supports controlled mode", async () => {
58
+ const user = userEvent.setup();
59
+
60
+ function Demo() {
61
+ const [value, setValue] = useState("react");
62
+
63
+ return (
64
+ <SegmentedControl
65
+ data={data}
66
+ value={value}
67
+ onChange={setValue}
68
+ />
69
+ );
70
+ }
71
+
72
+ await render(<Demo />);
73
+
74
+ await user.click(screen.getByRole("radio", { name: "Svelte" }));
75
+
76
+ expect(screen.getByRole("radio", { name: "Svelte" })).toHaveAttribute(
77
+ "aria-checked",
78
+ "true"
79
+ );
80
+ });
81
+
82
+ it("does not select disabled item", async () => {
83
+ const user = userEvent.setup();
84
+ const onChange = vi.fn();
85
+
86
+ await render(
87
+ <SegmentedControl
88
+ defaultValue="react"
89
+ onChange={onChange}
90
+ data={[
91
+ { value: "react", label: "React" },
92
+ { value: "vue", label: "Vue", disabled: true }
93
+ ]}
94
+ />
95
+ );
96
+
97
+ const disabledControl = screen.getByRole("radio", { name: "Vue" });
98
+
99
+ expect(disabledControl).toBeDisabled();
100
+
101
+ await user.click(disabledControl);
102
+
103
+ expect(onChange).not.toHaveBeenCalled();
104
+ expect(disabledControl).toHaveAttribute("aria-checked", "false");
105
+ });
106
+
107
+ it("supports keyboard navigation and skips disabled items", async () => {
108
+ const user = userEvent.setup();
109
+
110
+ await render(
111
+ <SegmentedControl
112
+ defaultValue="react"
113
+ data={[
114
+ { value: "react", label: "React" },
115
+ { value: "vue", label: "Vue", disabled: true },
116
+ { value: "svelte", label: "Svelte" }
117
+ ]}
118
+ />
119
+ );
120
+
121
+ const reactControl = screen.getByRole("radio", { name: "React" });
122
+ reactControl.focus();
123
+
124
+ await user.keyboard("{ArrowRight}");
125
+
126
+ const svelteControl = screen.getByRole("radio", { name: "Svelte" });
127
+
128
+ expect(svelteControl).toHaveFocus();
129
+ expect(svelteControl).toHaveAttribute("aria-checked", "true");
130
+ });
131
+
132
+ it("supports root and slot class names", async () => {
133
+ const { container } = await render(
134
+ <SegmentedControl
135
+ data={data}
136
+ className="custom-root"
137
+ classNames={{
138
+ control: "custom-control",
139
+ label: "custom-label"
140
+ }}
141
+ />
142
+ );
143
+
144
+ const root = container.firstElementChild as HTMLDivElement;
145
+ const control = screen.getByRole("radio", { name: "React" });
146
+ const label = screen.getByText("React");
147
+
148
+ expect(root).toHaveClass("custom-root");
149
+ expect(control).toHaveClass("custom-control");
150
+ expect(label).toHaveClass("custom-label");
151
+ });
152
+ });
@@ -0,0 +1,245 @@
1
+ import { useId, useUncontrolled } from "@refraktor/utils";
2
+ import { KeyboardEvent, useMemo, useRef } from "react";
3
+ import { useTheme } from "../../theme";
4
+ import {
5
+ createClassNamesConfig,
6
+ createComponentConfig,
7
+ factory,
8
+ useClassNames,
9
+ useProps
10
+ } from "../../utils";
11
+ import { getSize } from "./segmented-control.styles";
12
+ import {
13
+ SegmentedControlClassNames,
14
+ SegmentedControlFactoryPayload,
15
+ SegmentedControlProps
16
+ } from "./segmented-control.types";
17
+
18
+ const defaultProps = {
19
+ size: "md",
20
+ radius: "default",
21
+ fullWidth: false,
22
+ disabled: false
23
+ } satisfies Partial<SegmentedControlProps>;
24
+
25
+ const SegmentedControl = factory<SegmentedControlFactoryPayload>(
26
+ (_props, ref) => {
27
+ const { cx, getRadius } = useTheme();
28
+ const {
29
+ id,
30
+ data,
31
+ value,
32
+ defaultValue,
33
+ onChange,
34
+ size,
35
+ radius,
36
+ fullWidth,
37
+ disabled,
38
+ name,
39
+ className,
40
+ classNames,
41
+ ...props
42
+ } = useProps("SegmentedControl", defaultProps, _props);
43
+ const classes = useClassNames("SegmentedControl", classNames);
44
+ const _id = useId(id);
45
+
46
+ const firstEnabledValue = useMemo(
47
+ () => data.find((item) => !item.disabled)?.value ?? "",
48
+ [data]
49
+ );
50
+
51
+ const [selectedValue, setSelectedValue] = useUncontrolled<string>({
52
+ value,
53
+ defaultValue,
54
+ finalValue: firstEnabledValue,
55
+ onChange
56
+ });
57
+
58
+ const sizeStyles = getSize(size);
59
+ const controlRefs = useRef<Array<HTMLButtonElement | null>>([]);
60
+
61
+ const enabledIndexes = useMemo(() => {
62
+ const indexes: number[] = [];
63
+
64
+ data.forEach((item, index) => {
65
+ if (!item.disabled) {
66
+ indexes.push(index);
67
+ }
68
+ });
69
+
70
+ return indexes;
71
+ }, [data]);
72
+
73
+ const activeIndex = useMemo(
74
+ () => data.findIndex((item) => item.value === selectedValue),
75
+ [data, selectedValue]
76
+ );
77
+
78
+ const tabStopIndex = useMemo(() => {
79
+ if (activeIndex !== -1 && !data[activeIndex]?.disabled) {
80
+ return activeIndex;
81
+ }
82
+
83
+ return enabledIndexes[0] ?? -1;
84
+ }, [activeIndex, data, enabledIndexes]);
85
+
86
+ const selectByIndex = (index: number) => {
87
+ const item = data[index];
88
+
89
+ if (
90
+ !item ||
91
+ disabled ||
92
+ item.disabled ||
93
+ item.value === selectedValue
94
+ ) {
95
+ return;
96
+ }
97
+
98
+ setSelectedValue(item.value);
99
+ };
100
+
101
+ const moveSelection = (currentIndex: number, direction: 1 | -1) => {
102
+ if (disabled || enabledIndexes.length === 0) {
103
+ return;
104
+ }
105
+
106
+ const currentEnabledPosition = enabledIndexes.indexOf(currentIndex);
107
+ const basePosition =
108
+ currentEnabledPosition === -1 ? 0 : currentEnabledPosition;
109
+ const nextPosition =
110
+ (basePosition + direction + enabledIndexes.length) %
111
+ enabledIndexes.length;
112
+ const nextIndex = enabledIndexes[nextPosition];
113
+
114
+ selectByIndex(nextIndex);
115
+ controlRefs.current[nextIndex]?.focus();
116
+ };
117
+
118
+ const handleKeyDown = (
119
+ event: KeyboardEvent<HTMLButtonElement>,
120
+ index: number,
121
+ isControlDisabled: boolean
122
+ ) => {
123
+ if (event.defaultPrevented || disabled || isControlDisabled) {
124
+ return;
125
+ }
126
+
127
+ if (event.key === "ArrowRight" || event.key === "ArrowDown") {
128
+ event.preventDefault();
129
+ moveSelection(index, 1);
130
+ return;
131
+ }
132
+
133
+ if (event.key === "ArrowLeft" || event.key === "ArrowUp") {
134
+ event.preventDefault();
135
+ moveSelection(index, -1);
136
+ return;
137
+ }
138
+
139
+ if (event.key === "Home") {
140
+ event.preventDefault();
141
+
142
+ const firstEnabledIndex = enabledIndexes[0];
143
+
144
+ if (firstEnabledIndex !== undefined) {
145
+ selectByIndex(firstEnabledIndex);
146
+ controlRefs.current[firstEnabledIndex]?.focus();
147
+ }
148
+
149
+ return;
150
+ }
151
+
152
+ if (event.key === "End") {
153
+ event.preventDefault();
154
+
155
+ const lastEnabledIndex =
156
+ enabledIndexes[enabledIndexes.length - 1];
157
+
158
+ if (lastEnabledIndex !== undefined) {
159
+ selectByIndex(lastEnabledIndex);
160
+ controlRefs.current[lastEnabledIndex]?.focus();
161
+ }
162
+ }
163
+ };
164
+
165
+ return (
166
+ <div
167
+ ref={ref}
168
+ id={_id}
169
+ role="radiogroup"
170
+ data-disabled={disabled}
171
+ aria-disabled={disabled}
172
+ className={cx(
173
+ "relative inline-flex items-stretch border border-[var(--refraktor-border)]",
174
+ "bg-[var(--refraktor-bg-subtle)]",
175
+ "data-[disabled=true]:opacity-50",
176
+ fullWidth && "w-full",
177
+ sizeStyles.root,
178
+ getRadius(radius),
179
+ classes.root,
180
+ className
181
+ )}
182
+ {...props}
183
+ >
184
+ {data.map((item, index) => {
185
+ const isActive = item.value === selectedValue;
186
+ const isControlDisabled = !!(disabled || item.disabled);
187
+
188
+ return (
189
+ <button
190
+ key={item.value}
191
+ ref={(node) => {
192
+ controlRefs.current[index] = node;
193
+ }}
194
+ type="button"
195
+ role="radio"
196
+ id={`${_id}-control-${index}`}
197
+ aria-checked={isActive}
198
+ aria-disabled={isControlDisabled}
199
+ data-active={isActive}
200
+ data-disabled={isControlDisabled}
201
+ disabled={isControlDisabled}
202
+ tabIndex={
203
+ isControlDisabled || tabStopIndex !== index
204
+ ? -1
205
+ : 0
206
+ }
207
+ className={cx(
208
+ "relative inline-flex items-center justify-center whitespace-nowrap select-none outline-none transition-colors",
209
+ "focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--refraktor-primary)]",
210
+ "data-[disabled=true]:opacity-50 data-[disabled=true]:pointer-events-none data-[disabled=true]:cursor-not-allowed",
211
+ fullWidth && "flex-1",
212
+ sizeStyles.control,
213
+ sizeStyles.label,
214
+ getRadius(radius),
215
+ isActive
216
+ ? "bg-[var(--refraktor-primary)] text-[var(--refraktor-primary-text)]"
217
+ : "text-[var(--refraktor-text-secondary)] hover:bg-[var(--refraktor-bg-hover)] hover:text-[var(--refraktor-text)]",
218
+ classes.control
219
+ )}
220
+ onClick={() => selectByIndex(index)}
221
+ onKeyDown={(event) =>
222
+ handleKeyDown(event, index, isControlDisabled)
223
+ }
224
+ >
225
+ <span className={cx("leading-none", classes.label)}>
226
+ {item.label}
227
+ </span>
228
+ </button>
229
+ );
230
+ })}
231
+
232
+ {name && (
233
+ <input type="hidden" name={name} value={selectedValue} />
234
+ )}
235
+ </div>
236
+ );
237
+ }
238
+ );
239
+
240
+ SegmentedControl.displayName = "@refraktor/core/SegmentedControl";
241
+ SegmentedControl.configure = createComponentConfig<SegmentedControlProps>();
242
+ SegmentedControl.classNames =
243
+ createClassNamesConfig<SegmentedControlClassNames>();
244
+
245
+ export default SegmentedControl;