@refraktor/core 0.0.3 → 0.0.4

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 (284) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/build/components/button/button.styles.js +5 -5
  3. package/build/components/chip/chip-group/chip-group.d.ts +4 -0
  4. package/build/components/chip/chip-group/chip-group.d.ts.map +1 -0
  5. package/build/components/chip/chip-group/chip-group.js +43 -0
  6. package/build/components/chip/chip-group/index.d.ts +2 -0
  7. package/build/components/chip/chip-group/index.d.ts.map +1 -0
  8. package/build/components/chip/chip-group/index.js +1 -0
  9. package/build/components/chip/chip.context.d.ts +15 -0
  10. package/build/components/chip/chip.context.d.ts.map +1 -0
  11. package/build/components/chip/chip.context.js +2 -0
  12. package/build/components/chip/chip.d.ts +4 -0
  13. package/build/components/chip/chip.d.ts.map +1 -0
  14. package/build/components/chip/chip.js +85 -0
  15. package/build/components/chip/chip.styles.d.ts +5 -0
  16. package/build/components/chip/chip.styles.d.ts.map +1 -0
  17. package/build/components/chip/chip.styles.js +19 -0
  18. package/build/components/chip/chip.test.d.ts +2 -0
  19. package/build/components/chip/chip.test.d.ts.map +1 -0
  20. package/build/components/chip/chip.test.js +107 -0
  21. package/build/components/chip/chip.types.d.ts +95 -0
  22. package/build/components/chip/chip.types.d.ts.map +1 -0
  23. package/build/components/chip/chip.types.js +1 -0
  24. package/build/components/chip/index.d.ts +4 -0
  25. package/build/components/chip/index.d.ts.map +1 -0
  26. package/build/components/chip/index.js +2 -0
  27. package/build/components/combobox/combobox-dropdown/combobox-dropdown.d.ts +4 -0
  28. package/build/components/combobox/combobox-dropdown/combobox-dropdown.d.ts.map +1 -0
  29. package/build/components/combobox/combobox-dropdown/combobox-dropdown.js +21 -0
  30. package/build/components/combobox/combobox-dropdown/index.d.ts +2 -0
  31. package/build/components/combobox/combobox-dropdown/index.d.ts.map +1 -0
  32. package/build/components/combobox/combobox-dropdown/index.js +1 -0
  33. package/build/components/combobox/combobox-group/combobox-group.d.ts +4 -0
  34. package/build/components/combobox/combobox-group/combobox-group.d.ts.map +1 -0
  35. package/build/components/combobox/combobox-group/combobox-group.js +15 -0
  36. package/build/components/combobox/combobox-group/index.d.ts +2 -0
  37. package/build/components/combobox/combobox-group/index.d.ts.map +1 -0
  38. package/build/components/combobox/combobox-group/index.js +1 -0
  39. package/build/components/combobox/combobox-input/combobox-input.d.ts +4 -0
  40. package/build/components/combobox/combobox-input/combobox-input.d.ts.map +1 -0
  41. package/build/components/combobox/combobox-input/combobox-input.js +101 -0
  42. package/build/components/combobox/combobox-input/index.d.ts +2 -0
  43. package/build/components/combobox/combobox-input/index.d.ts.map +1 -0
  44. package/build/components/combobox/combobox-input/index.js +1 -0
  45. package/build/components/combobox/combobox-option/combobox-option.d.ts +4 -0
  46. package/build/components/combobox/combobox-option/combobox-option.d.ts.map +1 -0
  47. package/build/components/combobox/combobox-option/combobox-option.js +86 -0
  48. package/build/components/combobox/combobox-option/index.d.ts +2 -0
  49. package/build/components/combobox/combobox-option/index.d.ts.map +1 -0
  50. package/build/components/combobox/combobox-option/index.js +1 -0
  51. package/build/components/combobox/combobox-root/combobox-root.d.ts +4 -0
  52. package/build/components/combobox/combobox-root/combobox-root.d.ts.map +1 -0
  53. package/build/components/combobox/combobox-root/combobox-root.js +282 -0
  54. package/build/components/combobox/combobox-root/index.d.ts +2 -0
  55. package/build/components/combobox/combobox-root/index.d.ts.map +1 -0
  56. package/build/components/combobox/combobox-root/index.js +1 -0
  57. package/build/components/combobox/combobox.context.d.ts +73 -0
  58. package/build/components/combobox/combobox.context.d.ts.map +1 -0
  59. package/build/components/combobox/combobox.context.js +50 -0
  60. package/build/components/combobox/combobox.d.ts +4 -0
  61. package/build/components/combobox/combobox.d.ts.map +1 -0
  62. package/build/components/combobox/combobox.js +31 -0
  63. package/build/components/combobox/combobox.test.d.ts +2 -0
  64. package/build/components/combobox/combobox.test.d.ts.map +1 -0
  65. package/build/components/combobox/combobox.test.js +104 -0
  66. package/build/components/combobox/combobox.types.d.ts +205 -0
  67. package/build/components/combobox/combobox.types.d.ts.map +1 -0
  68. package/build/components/combobox/combobox.types.js +1 -0
  69. package/build/components/combobox/index.d.ts +8 -0
  70. package/build/components/combobox/index.d.ts.map +1 -0
  71. package/build/components/combobox/index.js +6 -0
  72. package/build/components/combobox/use-combobox.d.ts +32 -0
  73. package/build/components/combobox/use-combobox.d.ts.map +1 -0
  74. package/build/components/combobox/use-combobox.js +80 -0
  75. package/build/components/drawer/drawer-body/drawer-body.d.ts +4 -0
  76. package/build/components/drawer/drawer-body/drawer-body.d.ts.map +1 -0
  77. package/build/components/drawer/drawer-body/drawer-body.js +11 -0
  78. package/build/components/drawer/drawer-body/index.d.ts +2 -0
  79. package/build/components/drawer/drawer-body/index.d.ts.map +1 -0
  80. package/build/components/drawer/drawer-body/index.js +1 -0
  81. package/build/components/drawer/drawer-content/drawer-content.d.ts.map +1 -1
  82. package/build/components/drawer/drawer-content/drawer-content.js +9 -6
  83. package/build/components/drawer/drawer-overlay/drawer-overlay.d.ts.map +1 -1
  84. package/build/components/drawer/drawer-overlay/drawer-overlay.js +4 -4
  85. package/build/components/drawer/drawer-root/drawer-root.d.ts.map +1 -1
  86. package/build/components/drawer/drawer-root/drawer-root.js +8 -9
  87. package/build/components/drawer/drawer.context.d.ts +2 -1
  88. package/build/components/drawer/drawer.context.d.ts.map +1 -1
  89. package/build/components/drawer/drawer.d.ts.map +1 -1
  90. package/build/components/drawer/drawer.js +5 -3
  91. package/build/components/drawer/drawer.test.js +55 -16
  92. package/build/components/drawer/drawer.types.d.ts +29 -3
  93. package/build/components/drawer/drawer.types.d.ts.map +1 -1
  94. package/build/components/drawer/index.d.ts +2 -1
  95. package/build/components/drawer/index.d.ts.map +1 -1
  96. package/build/components/drawer/index.js +1 -0
  97. package/build/components/drawer/use-drawer.d.ts +8 -1
  98. package/build/components/drawer/use-drawer.d.ts.map +1 -1
  99. package/build/components/drawer/use-drawer.js +25 -38
  100. package/build/components/file-input/file-input.d.ts +4 -0
  101. package/build/components/file-input/file-input.d.ts.map +1 -0
  102. package/build/components/file-input/file-input.js +88 -0
  103. package/build/components/file-input/file-input.test.d.ts +2 -0
  104. package/build/components/file-input/file-input.test.d.ts.map +1 -0
  105. package/build/components/file-input/file-input.test.js +74 -0
  106. package/build/components/file-input/file-input.types.d.ts +46 -0
  107. package/build/components/file-input/file-input.types.d.ts.map +1 -0
  108. package/build/components/file-input/file-input.types.js +1 -0
  109. package/build/components/file-input/file-input.utils.d.ts +11 -0
  110. package/build/components/file-input/file-input.utils.d.ts.map +1 -0
  111. package/build/components/file-input/file-input.utils.js +67 -0
  112. package/build/components/file-input/file-input.utils.test.d.ts +2 -0
  113. package/build/components/file-input/file-input.utils.test.d.ts.map +1 -0
  114. package/build/components/file-input/file-input.utils.test.js +27 -0
  115. package/build/components/file-input/index.d.ts +3 -0
  116. package/build/components/file-input/index.d.ts.map +1 -0
  117. package/build/components/file-input/index.js +2 -0
  118. package/build/components/for/for.d.ts +8 -0
  119. package/build/components/for/for.d.ts.map +1 -0
  120. package/build/components/for/for.js +32 -0
  121. package/build/components/for/for.test.d.ts +2 -0
  122. package/build/components/for/for.test.d.ts.map +1 -0
  123. package/build/components/for/for.test.js +31 -0
  124. package/build/components/for/for.types.d.ts +33 -0
  125. package/build/components/for/for.types.d.ts.map +1 -0
  126. package/build/components/for/for.types.js +1 -0
  127. package/build/components/for/index.d.ts +3 -0
  128. package/build/components/for/index.d.ts.map +1 -0
  129. package/build/components/for/index.js +1 -0
  130. package/build/components/index.d.ts +6 -0
  131. package/build/components/index.d.ts.map +1 -1
  132. package/build/components/index.js +6 -0
  133. package/build/components/modal/index.d.ts +2 -1
  134. package/build/components/modal/index.d.ts.map +1 -1
  135. package/build/components/modal/index.js +1 -0
  136. package/build/components/modal/modal-body/index.d.ts +2 -0
  137. package/build/components/modal/modal-body/index.d.ts.map +1 -0
  138. package/build/components/modal/modal-body/index.js +1 -0
  139. package/build/components/modal/modal-body/modal-body.d.ts +4 -0
  140. package/build/components/modal/modal-body/modal-body.d.ts.map +1 -0
  141. package/build/components/modal/modal-body/modal-body.js +11 -0
  142. package/build/components/modal/modal-content/modal-content.d.ts.map +1 -1
  143. package/build/components/modal/modal-content/modal-content.js +13 -5
  144. package/build/components/modal/modal-header/modal-header.js +2 -2
  145. package/build/components/modal/modal-overlay/modal-overlay.d.ts.map +1 -1
  146. package/build/components/modal/modal-overlay/modal-overlay.js +4 -4
  147. package/build/components/modal/modal-root/modal-root.d.ts.map +1 -1
  148. package/build/components/modal/modal-root/modal-root.js +12 -9
  149. package/build/components/modal/modal.context.d.ts +5 -2
  150. package/build/components/modal/modal.context.d.ts.map +1 -1
  151. package/build/components/modal/modal.d.ts.map +1 -1
  152. package/build/components/modal/modal.js +5 -3
  153. package/build/components/modal/modal.test.js +78 -13
  154. package/build/components/modal/modal.types.d.ts +34 -5
  155. package/build/components/modal/modal.types.d.ts.map +1 -1
  156. package/build/components/modal/use-modal.d.ts +8 -1
  157. package/build/components/modal/use-modal.d.ts.map +1 -1
  158. package/build/components/modal/use-modal.js +25 -38
  159. package/build/components/password-input/index.d.ts +3 -0
  160. package/build/components/password-input/index.d.ts.map +1 -0
  161. package/build/components/password-input/index.js +2 -0
  162. package/build/components/password-input/password-input.d.ts +4 -0
  163. package/build/components/password-input/password-input.d.ts.map +1 -0
  164. package/build/components/password-input/password-input.js +32 -0
  165. package/build/components/password-input/password-input.test.d.ts +2 -0
  166. package/build/components/password-input/password-input.test.d.ts.map +1 -0
  167. package/build/components/password-input/password-input.test.js +47 -0
  168. package/build/components/password-input/password-input.types.d.ts +24 -0
  169. package/build/components/password-input/password-input.types.d.ts.map +1 -0
  170. package/build/components/password-input/password-input.types.js +1 -0
  171. package/build/components/pin-input/index.d.ts +3 -0
  172. package/build/components/pin-input/index.d.ts.map +1 -0
  173. package/build/components/pin-input/index.js +2 -0
  174. package/build/components/pin-input/pin-input.d.ts +4 -0
  175. package/build/components/pin-input/pin-input.d.ts.map +1 -0
  176. package/build/components/pin-input/pin-input.js +245 -0
  177. package/build/components/pin-input/pin-input.test.d.ts +2 -0
  178. package/build/components/pin-input/pin-input.test.d.ts.map +1 -0
  179. package/build/components/pin-input/pin-input.test.js +87 -0
  180. package/build/components/pin-input/pin-input.types.d.ts +44 -0
  181. package/build/components/pin-input/pin-input.types.d.ts.map +1 -0
  182. package/build/components/pin-input/pin-input.types.js +1 -0
  183. package/build/components/scroll-area/index.d.ts +3 -0
  184. package/build/components/scroll-area/index.d.ts.map +1 -0
  185. package/build/components/scroll-area/index.js +1 -0
  186. package/build/components/scroll-area/scroll-area.d.ts +4 -0
  187. package/build/components/scroll-area/scroll-area.d.ts.map +1 -0
  188. package/build/components/scroll-area/scroll-area.js +30 -0
  189. package/build/components/scroll-area/scroll-area.test.d.ts +2 -0
  190. package/build/components/scroll-area/scroll-area.test.d.ts.map +1 -0
  191. package/build/components/scroll-area/scroll-area.test.js +39 -0
  192. package/build/components/scroll-area/scroll-area.types.d.ts +25 -0
  193. package/build/components/scroll-area/scroll-area.types.d.ts.map +1 -0
  194. package/build/components/scroll-area/scroll-area.types.js +1 -0
  195. package/build/components/segmented-control/segmented-control.d.ts.map +1 -1
  196. package/build/components/segmented-control/segmented-control.js +3 -3
  197. package/build/components/segmented-control/segmented-control.styles.js +13 -13
  198. package/build/components/segmented-control/segmented-control.test.js +11 -0
  199. package/build/components/segmented-control/segmented-control.types.d.ts +2 -0
  200. package/build/components/segmented-control/segmented-control.types.d.ts.map +1 -1
  201. package/build/icons/eye-off.d.ts +4 -0
  202. package/build/icons/eye-off.d.ts.map +1 -0
  203. package/build/icons/eye-off.js +5 -0
  204. package/build/icons/eye.d.ts +4 -0
  205. package/build/icons/eye.d.ts.map +1 -0
  206. package/build/icons/eye.js +5 -0
  207. package/build/icons/index.d.ts +2 -0
  208. package/build/icons/index.d.ts.map +1 -1
  209. package/build/icons/index.js +2 -0
  210. package/build/style.css +1 -1
  211. package/package.json +2 -2
  212. package/src/components/button/button.styles.ts +5 -5
  213. package/src/components/chip/chip-group/chip-group.tsx +107 -0
  214. package/src/components/chip/chip-group/index.ts +1 -0
  215. package/src/components/chip/chip.context.ts +15 -0
  216. package/src/components/chip/chip.styles.ts +36 -0
  217. package/src/components/chip/chip.test.tsx +197 -0
  218. package/src/components/chip/chip.tsx +208 -0
  219. package/src/components/chip/chip.types.ts +134 -0
  220. package/src/components/chip/index.ts +10 -0
  221. package/src/components/drawer/drawer-body/drawer-body.tsx +29 -0
  222. package/src/components/drawer/drawer-body/index.ts +1 -0
  223. package/src/components/drawer/drawer-content/drawer-content.tsx +63 -26
  224. package/src/components/drawer/drawer-overlay/drawer-overlay.tsx +6 -5
  225. package/src/components/drawer/drawer-root/drawer-root.tsx +17 -18
  226. package/src/components/drawer/drawer.context.ts +2 -1
  227. package/src/components/drawer/drawer.test.tsx +144 -36
  228. package/src/components/drawer/drawer.tsx +31 -3
  229. package/src/components/drawer/drawer.types.ts +37 -3
  230. package/src/components/drawer/index.ts +2 -0
  231. package/src/components/drawer/use-drawer.ts +44 -51
  232. package/src/components/file-input/file-input.test.tsx +134 -0
  233. package/src/components/file-input/file-input.tsx +224 -0
  234. package/src/components/file-input/file-input.types.ts +78 -0
  235. package/src/components/file-input/file-input.utils.test.ts +36 -0
  236. package/src/components/file-input/file-input.utils.ts +130 -0
  237. package/src/components/file-input/index.ts +2 -0
  238. package/src/components/for/for.test.tsx +66 -0
  239. package/src/components/for/for.tsx +53 -0
  240. package/src/components/for/for.types.ts +40 -0
  241. package/src/components/for/index.ts +2 -0
  242. package/src/components/index.ts +6 -0
  243. package/src/components/menu/menu-dropdown/menu-dropdown.tsx +220 -220
  244. package/src/components/menu/menu-sub-dropdown/menu-sub-dropdown.tsx +221 -221
  245. package/src/components/modal/index.ts +4 -1
  246. package/src/components/modal/modal-body/index.ts +1 -0
  247. package/src/components/modal/modal-body/modal-body.tsx +29 -0
  248. package/src/components/modal/modal-content/modal-content.tsx +71 -24
  249. package/src/components/modal/modal-header/modal-header.tsx +2 -2
  250. package/src/components/modal/modal-overlay/modal-overlay.tsx +46 -45
  251. package/src/components/modal/modal-root/modal-root.tsx +22 -17
  252. package/src/components/modal/modal.context.ts +5 -2
  253. package/src/components/modal/modal.test.tsx +234 -64
  254. package/src/components/modal/modal.tsx +36 -4
  255. package/src/components/modal/modal.types.ts +49 -8
  256. package/src/components/modal/use-modal.ts +44 -51
  257. package/src/components/password-input/index.ts +2 -0
  258. package/src/components/password-input/password-input.test.tsx +72 -0
  259. package/src/components/password-input/password-input.tsx +85 -0
  260. package/src/components/password-input/password-input.types.ts +30 -0
  261. package/src/components/pin-input/index.ts +2 -0
  262. package/src/components/pin-input/pin-input.test.tsx +149 -0
  263. package/src/components/pin-input/pin-input.tsx +473 -0
  264. package/src/components/pin-input/pin-input.types.ts +78 -0
  265. package/src/components/scroll-area/index.ts +6 -0
  266. package/src/components/scroll-area/scroll-area.test.tsx +72 -0
  267. package/src/components/scroll-area/scroll-area.tsx +70 -0
  268. package/src/components/scroll-area/scroll-area.types.ts +37 -0
  269. package/src/components/segmented-control/segmented-control.styles.ts +13 -13
  270. package/src/components/segmented-control/segmented-control.test.tsx +18 -0
  271. package/src/components/segmented-control/segmented-control.tsx +11 -1
  272. package/src/components/segmented-control/segmented-control.types.ts +3 -0
  273. package/src/components/select/select-dropdown/select-dropdown.tsx +299 -299
  274. package/src/components/select/select-root/select-root.tsx +333 -333
  275. package/src/components/select/select-trigger/select-trigger.tsx +123 -123
  276. package/src/components/select/select.context.ts +140 -140
  277. package/src/components/select/select.test.tsx +190 -190
  278. package/src/components/select/select.types.ts +272 -272
  279. package/src/components/select/use-select.ts +170 -170
  280. package/src/icons/eye-off.tsx +30 -0
  281. package/src/icons/eye.tsx +24 -0
  282. package/src/icons/index.ts +2 -0
  283. package/src/style.css +14 -8
  284. package/tsconfig.tsbuildinfo +1 -1
@@ -1,6 +1,9 @@
1
+ import {
2
+ FloatingFocusManager,
3
+ FloatingPortal
4
+ } from "@floating-ui/react";
1
5
  import { useTheme } from "../../../theme";
2
6
  import { factory } from "../../../utils";
3
- import { Portal } from "../../portal";
4
7
  import { Transition, TransitionProps } from "../../transition";
5
8
  import { useDrawerContext } from "../drawer.context";
6
9
  import { DrawerContentFactoryPayload, DrawerPosition } from "../drawer.types";
@@ -32,14 +35,15 @@ const DrawerContent = factory<DrawerContentFactoryPayload>(
32
35
  radius,
33
36
  position,
34
37
  size,
38
+ trapFocus,
39
+ returnFocus,
35
40
  transitionProps,
36
41
  headerId,
37
- contentRef,
38
42
  getStyles
39
43
  } = useDrawerContext();
40
44
 
41
45
  const setRefs = (node: HTMLDivElement | null) => {
42
- contentRef.current = node;
46
+ drawer.refs.setFloating(node);
43
47
 
44
48
  if (typeof ref === "function") {
45
49
  ref(node);
@@ -64,32 +68,65 @@ const DrawerContent = factory<DrawerContentFactoryPayload>(
64
68
  transitionClassName
65
69
  )}
66
70
  >
67
- <div
68
- ref={setRefs}
69
- role="dialog"
70
- aria-modal="true"
71
- aria-labelledby={headerId}
72
- data-opened={drawer.opened}
73
- data-position={position}
74
- style={{
75
- ...sizeStyles,
76
- ...style
77
- }}
78
- className={cx(
79
- "pointer-events-auto fixed z-50 border border-[var(--refraktor-border)] bg-[var(--refraktor-bg)] p-4 text-[var(--refraktor-text)] shadow-md",
80
- positionClasses[position],
81
- getRadius(radius),
82
- getStyles("content"),
83
- className
84
- )}
85
- {...props}
86
- >
87
- {children}
88
- </div>
71
+ {drawer.opened ? (
72
+ <FloatingFocusManager
73
+ context={drawer.context}
74
+ modal={trapFocus}
75
+ returnFocus={returnFocus}
76
+ outsideElementsInert={false}
77
+ >
78
+ <div
79
+ ref={setRefs}
80
+ aria-labelledby={headerId}
81
+ data-opened={drawer.opened}
82
+ data-position={position}
83
+ style={{
84
+ ...sizeStyles,
85
+ ...style
86
+ }}
87
+ className={cx(
88
+ "pointer-events-auto fixed z-50 border border-[var(--refraktor-border)] bg-[var(--refraktor-bg)] p-4 text-[var(--refraktor-text)] shadow-md",
89
+ positionClasses[position],
90
+ getRadius(radius),
91
+ getStyles("content"),
92
+ className
93
+ )}
94
+ {...drawer.getFloatingProps()}
95
+ {...props}
96
+ >
97
+ {children}
98
+ </div>
99
+ </FloatingFocusManager>
100
+ ) : (
101
+ <div
102
+ ref={setRefs}
103
+ aria-labelledby={headerId}
104
+ data-opened={drawer.opened}
105
+ data-position={position}
106
+ style={{
107
+ ...sizeStyles,
108
+ ...style
109
+ }}
110
+ className={cx(
111
+ "pointer-events-auto fixed z-50 border border-[var(--refraktor-border)] bg-[var(--refraktor-bg)] p-4 text-[var(--refraktor-text)] shadow-md",
112
+ positionClasses[position],
113
+ getRadius(radius),
114
+ getStyles("content"),
115
+ className
116
+ )}
117
+ {...props}
118
+ >
119
+ {children}
120
+ </div>
121
+ )}
89
122
  </Transition>
90
123
  );
91
124
 
92
- return withinPortal ? <Portal>{content}</Portal> : content;
125
+ return withinPortal ? (
126
+ <FloatingPortal>{content}</FloatingPortal>
127
+ ) : (
128
+ content
129
+ );
93
130
  }
94
131
  );
95
132
 
@@ -1,6 +1,6 @@
1
+ import { FloatingOverlay, FloatingPortal } from "@floating-ui/react";
1
2
  import { useTheme } from "../../../theme";
2
3
  import { factory } from "../../../utils";
3
- import { Portal } from "../../portal";
4
4
  import { Transition } from "../../transition";
5
5
  import { useDrawerContext } from "../drawer.context";
6
6
  import { DrawerOverlayFactoryPayload } from "../drawer.types";
@@ -22,6 +22,7 @@ const DrawerOverlay = factory<DrawerOverlayFactoryPayload>(
22
22
  const {
23
23
  drawer,
24
24
  closeOnClickOutside,
25
+ lockScroll,
25
26
  withinPortal,
26
27
  transitionProps,
27
28
  getStyles
@@ -54,11 +55,11 @@ const DrawerOverlay = factory<DrawerOverlayFactoryPayload>(
54
55
  mounted={drawer.opened}
55
56
  {...transitionProps}
56
57
  >
57
- <div
58
+ <FloatingOverlay
59
+ lockScroll={lockScroll}
58
60
  ref={ref}
59
- aria-hidden="true"
60
61
  className={cx(
61
- "fixed inset-0 z-40",
62
+ "z-40",
62
63
  getStyles("overlay"),
63
64
  className
64
65
  )}
@@ -74,7 +75,7 @@ const DrawerOverlay = factory<DrawerOverlayFactoryPayload>(
74
75
  );
75
76
 
76
77
  return withinPortal ? (
77
- <Portal>{overlayContent}</Portal>
78
+ <FloatingPortal>{overlayContent}</FloatingPortal>
78
79
  ) : (
79
80
  overlayContent
80
81
  );
@@ -1,15 +1,13 @@
1
1
  import { useId } from "@refraktor/utils";
2
- import { useRef } from "react";
3
- import { RemoveScroll } from "react-remove-scroll";
4
2
  import { useTheme } from "../../../theme";
5
3
  import { factory, useClassNames, useProps } from "../../../utils";
6
4
  import { DrawerProvider } from "../drawer.context";
5
+ import { useDrawer } from "../use-drawer";
7
6
  import {
8
7
  DrawerClassNames,
9
8
  DrawerRootFactoryPayload,
10
9
  DrawerRootProps
11
10
  } from "../drawer.types";
12
- import { useDrawer } from "../use-drawer";
13
11
 
14
12
  const defaultProps = {
15
13
  closeOnClickOutside: true,
@@ -18,7 +16,9 @@ const defaultProps = {
18
16
  withinPortal: true,
19
17
  radius: "none",
20
18
  position: "right",
21
- size: "md"
19
+ size: "md",
20
+ trapFocus: true,
21
+ returnFocus: true
22
22
  } satisfies Partial<DrawerRootProps>;
23
23
 
24
24
  const DrawerRoot = factory<DrawerRootFactoryPayload>((_props, ref) => {
@@ -36,6 +36,8 @@ const DrawerRoot = factory<DrawerRootFactoryPayload>((_props, ref) => {
36
36
  radius,
37
37
  position,
38
38
  size,
39
+ trapFocus,
40
+ returnFocus,
39
41
  transitionProps,
40
42
  className,
41
43
  classNames,
@@ -45,15 +47,13 @@ const DrawerRoot = factory<DrawerRootFactoryPayload>((_props, ref) => {
45
47
 
46
48
  const _id = useId(id);
47
49
  const headerId = `${_id}-header`;
48
- const contentRef = useRef<HTMLDivElement | null>(null);
49
50
 
50
51
  const drawer = useDrawer({
51
52
  opened,
52
53
  defaultOpened,
53
54
  onOpenedChange,
54
55
  closeOnClickOutside,
55
- closeOnEscape,
56
- contentRef
56
+ closeOnEscape
57
57
  });
58
58
 
59
59
  const getStyles = (part: keyof DrawerClassNames) => classes[part];
@@ -68,23 +68,22 @@ const DrawerRoot = factory<DrawerRootFactoryPayload>((_props, ref) => {
68
68
  radius,
69
69
  position,
70
70
  size,
71
+ trapFocus,
72
+ returnFocus,
71
73
  transitionProps,
72
74
  headerId,
73
- contentRef,
74
75
  classNames,
75
76
  getStyles
76
77
  }}
77
78
  >
78
- <RemoveScroll enabled={drawer.opened && lockScroll}>
79
- <div
80
- ref={ref}
81
- id={_id}
82
- className={cx(classes.root, className)}
83
- {...props}
84
- >
85
- {children}
86
- </div>
87
- </RemoveScroll>
79
+ <div
80
+ ref={ref}
81
+ id={_id}
82
+ className={cx(classes.root, className)}
83
+ {...props}
84
+ >
85
+ {children}
86
+ </div>
88
87
  </DrawerProvider>
89
88
  );
90
89
  });
@@ -12,9 +12,10 @@ export interface DrawerContextValue {
12
12
  radius: RefraktorRadius;
13
13
  position: DrawerPosition;
14
14
  size: RefraktorSize;
15
+ trapFocus: boolean;
16
+ returnFocus: boolean;
15
17
  transitionProps?: Omit<TransitionProps, "children" | "mounted">;
16
18
  headerId: string;
17
- contentRef: React.MutableRefObject<HTMLDivElement | null>;
18
19
  classNames?: DrawerClassNames;
19
20
  getStyles: (part: keyof DrawerClassNames) => string | undefined;
20
21
  }
@@ -1,6 +1,7 @@
1
1
  import { describe, expect, it, vi } from "vitest";
2
2
  import { render, screen, userEvent, waitFor } from "../../vitest";
3
3
  import Drawer from "./drawer";
4
+ import { DrawerBody } from "./drawer-body";
4
5
  import { DrawerContent } from "./drawer-content";
5
6
  import { DrawerOverlay } from "./drawer-overlay";
6
7
  import { DrawerRoot } from "./drawer-root";
@@ -15,14 +16,16 @@ describe("@refraktor/core/Drawer", () => {
15
16
  const user = userEvent.setup();
16
17
 
17
18
  await render(
18
- <Drawer defaultOpened transitionProps={transitionProps}>
19
+ <Drawer.Root defaultOpened transitionProps={transitionProps}>
19
20
  <Drawer.Overlay />
20
21
 
21
22
  <Drawer.Content>
22
23
  <Drawer.Header text="Edit profile" />
23
- <p>Drawer body</p>
24
+ <Drawer.Body>
25
+ <p>Drawer body</p>
26
+ </Drawer.Body>
24
27
  </Drawer.Content>
25
- </Drawer>
28
+ </Drawer.Root>
26
29
  );
27
30
 
28
31
  expect(
@@ -41,14 +44,14 @@ describe("@refraktor/core/Drawer", () => {
41
44
  const onOpenedChange = vi.fn();
42
45
 
43
46
  await render(
44
- <Drawer
47
+ <Drawer.Root
45
48
  opened
46
49
  onOpenedChange={onOpenedChange}
47
50
  transitionProps={transitionProps}
48
51
  >
49
52
  <Drawer.Overlay data-testid="overlay" />
50
53
  <Drawer.Content>Controlled drawer</Drawer.Content>
51
- </Drawer>
54
+ </Drawer.Root>
52
55
  );
53
56
 
54
57
  await user.click(await screen.findByTestId("overlay"));
@@ -60,9 +63,9 @@ describe("@refraktor/core/Drawer", () => {
60
63
  const user = userEvent.setup();
61
64
 
62
65
  await render(
63
- <Drawer defaultOpened transitionProps={transitionProps}>
66
+ <Drawer.Root defaultOpened transitionProps={transitionProps}>
64
67
  <Drawer.Content>Keyboard close</Drawer.Content>
65
- </Drawer>
68
+ </Drawer.Root>
66
69
  );
67
70
 
68
71
  await screen.findByRole("dialog");
@@ -87,14 +90,14 @@ describe("@refraktor/core/Drawer", () => {
87
90
 
88
91
  it("applies custom overlay background opacity and blur", async () => {
89
92
  await render(
90
- <Drawer defaultOpened transitionProps={transitionProps}>
93
+ <Drawer.Root defaultOpened transitionProps={transitionProps}>
91
94
  <Drawer.Overlay
92
95
  data-testid="overlay"
93
96
  backgroundOpacity={0.4}
94
97
  blur={6}
95
98
  />
96
99
  <Drawer.Content>Styled overlay</Drawer.Content>
97
- </Drawer>
100
+ </Drawer.Root>
98
101
  );
99
102
 
100
103
  const overlay = await screen.findByTestId("overlay");
@@ -105,33 +108,10 @@ describe("@refraktor/core/Drawer", () => {
105
108
  });
106
109
  });
107
110
 
108
- it("locks and unlocks body scroll when enabled", async () => {
109
- const user = userEvent.setup();
110
-
111
- await render(
112
- <Drawer defaultOpened lockScroll transitionProps={transitionProps}>
113
- <Drawer.Content>
114
- Scroll locked
115
- <Drawer.Close />
116
- </Drawer.Content>
117
- </Drawer>
118
- );
119
-
120
- await waitFor(() => {
121
- expect(document.body).toHaveAttribute("data-scroll-locked");
122
- });
123
-
124
- await user.click(screen.getByRole("button", { name: "Close" }));
125
-
126
- await waitFor(() => {
127
- expect(document.body).not.toHaveAttribute("data-scroll-locked");
128
- });
129
- });
130
-
131
111
  it("supports different positions and predefined sizes", async () => {
132
112
  await render(
133
113
  <>
134
- <Drawer
114
+ <Drawer.Root
135
115
  defaultOpened
136
116
  position="left"
137
117
  size="sm"
@@ -140,9 +120,9 @@ describe("@refraktor/core/Drawer", () => {
140
120
  <Drawer.Content data-testid="left-content">
141
121
  Left
142
122
  </Drawer.Content>
143
- </Drawer>
123
+ </Drawer.Root>
144
124
 
145
- <Drawer
125
+ <Drawer.Root
146
126
  defaultOpened
147
127
  position="bottom"
148
128
  size="xl"
@@ -151,7 +131,7 @@ describe("@refraktor/core/Drawer", () => {
151
131
  <Drawer.Content data-testid="bottom-content">
152
132
  Bottom
153
133
  </Drawer.Content>
154
- </Drawer>
134
+ </Drawer.Root>
155
135
  </>
156
136
  );
157
137
 
@@ -163,4 +143,132 @@ describe("@refraktor/core/Drawer", () => {
163
143
  expect(bottomContent).toHaveAttribute("data-position", "bottom");
164
144
  expect(bottomContent).toHaveStyle({ height: "36rem" });
165
145
  });
146
+
147
+ it("renders Drawer.Body subcomponent", async () => {
148
+ await render(
149
+ <Drawer.Root defaultOpened transitionProps={transitionProps}>
150
+ <Drawer.Content>
151
+ <Drawer.Body data-testid="body">Body content</Drawer.Body>
152
+ </Drawer.Content>
153
+ </Drawer.Root>
154
+ );
155
+
156
+ const body = await screen.findByTestId("body");
157
+ expect(body).toBeInTheDocument();
158
+ expect(body).toHaveTextContent("Body content");
159
+ });
160
+
161
+ it("renders standalone DrawerBody component", async () => {
162
+ await render(
163
+ <DrawerRoot defaultOpened transitionProps={transitionProps}>
164
+ <DrawerContent>
165
+ <DrawerBody data-testid="body">Standalone body</DrawerBody>
166
+ </DrawerContent>
167
+ </DrawerRoot>
168
+ );
169
+
170
+ expect(await screen.findByTestId("body")).toHaveTextContent(
171
+ "Standalone body"
172
+ );
173
+ });
174
+
175
+ describe("single-component shorthand API", () => {
176
+ it("renders with title, overlay, close button, and body", async () => {
177
+ await render(
178
+ <Drawer
179
+ defaultOpened
180
+ title="Edit profile"
181
+ transitionProps={transitionProps}
182
+ >
183
+ <p>Profile form</p>
184
+ </Drawer>
185
+ );
186
+
187
+ expect(
188
+ await screen.findByRole("dialog", { name: "Edit profile" })
189
+ ).toBeInTheDocument();
190
+ expect(screen.getByText("Profile form")).toBeInTheDocument();
191
+ expect(
192
+ screen.getByRole("button", { name: "Close" })
193
+ ).toBeInTheDocument();
194
+ });
195
+
196
+ it("hides overlay when withOverlay is false", async () => {
197
+ const { container } = await render(
198
+ <Drawer
199
+ defaultOpened
200
+ title="No overlay"
201
+ withOverlay={false}
202
+ transitionProps={transitionProps}
203
+ />
204
+ );
205
+
206
+ expect(await screen.findByRole("dialog")).toBeInTheDocument();
207
+ expect(
208
+ container.ownerDocument.querySelector("[aria-hidden='true']")
209
+ ).toBeNull();
210
+ });
211
+
212
+ it("hides close button when withCloseButton is false", async () => {
213
+ await render(
214
+ <Drawer
215
+ defaultOpened
216
+ title="No close"
217
+ withCloseButton={false}
218
+ transitionProps={transitionProps}
219
+ />
220
+ );
221
+
222
+ await screen.findByRole("dialog");
223
+ expect(
224
+ screen.queryByRole("button", { name: "Close" })
225
+ ).not.toBeInTheDocument();
226
+ });
227
+
228
+ it("closes via shorthand close button", async () => {
229
+ const user = userEvent.setup();
230
+
231
+ await render(
232
+ <Drawer
233
+ defaultOpened
234
+ title="Closeable"
235
+ transitionProps={transitionProps}
236
+ >
237
+ Content
238
+ </Drawer>
239
+ );
240
+
241
+ await screen.findByRole("dialog");
242
+
243
+ await user.click(screen.getByRole("button", { name: "Close" }));
244
+
245
+ await waitFor(() => {
246
+ expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
247
+ });
248
+ });
249
+
250
+ it("passes overlayProps to the overlay", async () => {
251
+ await render(
252
+ <Drawer
253
+ defaultOpened
254
+ title="Custom overlay"
255
+ overlayProps={{
256
+ backgroundOpacity: 0.8,
257
+ blur: 10,
258
+ "data-testid": "shorthand-overlay"
259
+ } as any}
260
+ transitionProps={transitionProps}
261
+ >
262
+ Content
263
+ </Drawer>
264
+ );
265
+
266
+ const overlay = await screen.findByTestId("shorthand-overlay");
267
+
268
+ expect(overlay).toHaveStyle({
269
+ backgroundColor: "rgba(0, 0, 0, 0.8)",
270
+ backdropFilter: "blur(10px)"
271
+ });
272
+ });
273
+ });
166
274
  });
@@ -3,6 +3,7 @@ import {
3
3
  createComponentConfig,
4
4
  factory
5
5
  } from "../../utils";
6
+ import { DrawerBody } from "./drawer-body";
6
7
  import { DrawerClose } from "./drawer-close";
7
8
  import { DrawerContent } from "./drawer-content";
8
9
  import { DrawerHeader } from "./drawer-header";
@@ -14,9 +15,35 @@ import {
14
15
  DrawerProps
15
16
  } from "./drawer.types";
16
17
 
17
- const Drawer = factory<DrawerFactoryPayload>((props, ref) => {
18
- return <DrawerRoot {...props} ref={ref} />;
19
- });
18
+ const Drawer = factory<DrawerFactoryPayload>(
19
+ (
20
+ {
21
+ title,
22
+ withOverlay = true,
23
+ withCloseButton = true,
24
+ overlayProps,
25
+ children,
26
+ ...rootProps
27
+ },
28
+ ref
29
+ ) => {
30
+ return (
31
+ <DrawerRoot {...rootProps} ref={ref}>
32
+ {withOverlay && <DrawerOverlay {...overlayProps} />}
33
+
34
+ <DrawerContent>
35
+ {(title || withCloseButton) && (
36
+ <DrawerHeader withClose={withCloseButton}>
37
+ {title}
38
+ </DrawerHeader>
39
+ )}
40
+
41
+ <DrawerBody>{children}</DrawerBody>
42
+ </DrawerContent>
43
+ </DrawerRoot>
44
+ );
45
+ }
46
+ );
20
47
 
21
48
  Drawer.displayName = "@refraktor/core/Drawer";
22
49
  Drawer.configure = createComponentConfig<DrawerProps>();
@@ -25,6 +52,7 @@ Drawer.Root = DrawerRoot;
25
52
  Drawer.Overlay = DrawerOverlay;
26
53
  Drawer.Content = DrawerContent;
27
54
  Drawer.Header = DrawerHeader;
55
+ Drawer.Body = DrawerBody;
28
56
  Drawer.Close = DrawerClose;
29
57
 
30
58
  export default Drawer;
@@ -10,6 +10,7 @@ import { DrawerRoot } from "./drawer-root";
10
10
  import { DrawerOverlay } from "./drawer-overlay";
11
11
  import { DrawerContent } from "./drawer-content";
12
12
  import { DrawerHeader } from "./drawer-header";
13
+ import { DrawerBody } from "./drawer-body";
13
14
  import { DrawerClose } from "./drawer-close";
14
15
 
15
16
  export type DrawerPosition = "left" | "right" | "top" | "bottom";
@@ -19,12 +20,13 @@ export type DrawerClassNames = {
19
20
  overlay?: string;
20
21
  content?: string;
21
22
  header?: string;
23
+ body?: string;
22
24
  close?: string;
23
25
  };
24
26
 
25
- export interface DrawerProps extends ComponentPropsWithoutRef<"div"> {
27
+ export interface DrawerRootProps extends ComponentPropsWithoutRef<"div"> {
26
28
  /** Children containing drawer subcomponents */
27
- children: ReactNode;
29
+ children?: ReactNode;
28
30
 
29
31
  /** State of the drawer (controlled) */
30
32
  opened?: boolean;
@@ -56,6 +58,12 @@ export interface DrawerProps extends ComponentPropsWithoutRef<"div"> {
56
58
  /** Drawer size scale (width for left/right, height for top/bottom) @default `md` */
57
59
  size?: RefraktorSize;
58
60
 
61
+ /** Whether to trap focus within the drawer @default `true` */
62
+ trapFocus?: boolean;
63
+
64
+ /** Whether to return focus to trigger after close @default `true` */
65
+ returnFocus?: boolean;
66
+
59
67
  /** Transition props for overlay/content, uses Transition internally */
60
68
  transitionProps?: Omit<TransitionProps, "children" | "mounted">;
61
69
 
@@ -66,7 +74,19 @@ export interface DrawerProps extends ComponentPropsWithoutRef<"div"> {
66
74
  classNames?: DrawerClassNames;
67
75
  }
68
76
 
69
- export type DrawerRootProps = DrawerProps;
77
+ export interface DrawerProps extends Omit<DrawerRootProps, "title"> {
78
+ /** Title text rendered in the header */
79
+ title?: ReactNode;
80
+
81
+ /** Whether to render the overlay @default `true` */
82
+ withOverlay?: boolean;
83
+
84
+ /** Whether to show the close button in the header @default `true` */
85
+ withCloseButton?: boolean;
86
+
87
+ /** Props passed to the Overlay subcomponent */
88
+ overlayProps?: DrawerOverlayProps;
89
+ }
70
90
 
71
91
  export interface DrawerOverlayProps extends ComponentPropsWithoutRef<"div"> {
72
92
  /** Whether clicking the overlay closes drawer @default `true` */
@@ -104,6 +124,14 @@ export interface DrawerHeaderProps extends ComponentPropsWithoutRef<"div"> {
104
124
  className?: string;
105
125
  }
106
126
 
127
+ export interface DrawerBodyProps extends ComponentPropsWithoutRef<"div"> {
128
+ /** Body content */
129
+ children?: ReactNode;
130
+
131
+ /** Used for editing root class name */
132
+ className?: string;
133
+ }
134
+
107
135
  export interface DrawerCloseProps extends Omit<
108
136
  ComponentPropsWithoutRef<"button">,
109
137
  "onClick"
@@ -128,6 +156,7 @@ export interface DrawerFactoryPayload extends FactoryPayload {
128
156
  Overlay: typeof DrawerOverlay;
129
157
  Content: typeof DrawerContent;
130
158
  Header: typeof DrawerHeader;
159
+ Body: typeof DrawerBody;
131
160
  Close: typeof DrawerClose;
132
161
  };
133
162
  }
@@ -152,6 +181,11 @@ export interface DrawerHeaderFactoryPayload extends FactoryPayload {
152
181
  ref: HTMLDivElement;
153
182
  }
154
183
 
184
+ export interface DrawerBodyFactoryPayload extends FactoryPayload {
185
+ props: DrawerBodyProps;
186
+ ref: HTMLDivElement;
187
+ }
188
+
155
189
  export interface DrawerCloseFactoryPayload extends FactoryPayload {
156
190
  props: DrawerCloseProps;
157
191
  ref: HTMLButtonElement;
@@ -3,6 +3,7 @@ export { DrawerRoot } from "./drawer-root";
3
3
  export { DrawerOverlay } from "./drawer-overlay";
4
4
  export { DrawerContent } from "./drawer-content";
5
5
  export { DrawerHeader } from "./drawer-header";
6
+ export { DrawerBody } from "./drawer-body";
6
7
  export { DrawerClose } from "./drawer-close";
7
8
  export type {
8
9
  DrawerProps,
@@ -10,6 +11,7 @@ export type {
10
11
  DrawerOverlayProps,
11
12
  DrawerContentProps,
12
13
  DrawerHeaderProps,
14
+ DrawerBodyProps,
13
15
  DrawerCloseProps,
14
16
  DrawerClassNames,
15
17
  DrawerPosition