@tcn/ui 0.16.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (437) hide show
  1. package/dist/actions/index.d.ts +0 -1
  2. package/dist/actions/index.d.ts.map +1 -1
  3. package/dist/actions/index.js +6 -8
  4. package/dist/actions/index.js.map +1 -1
  5. package/dist/card.css +1 -0
  6. package/dist/column.css +1 -1
  7. package/dist/containers.css +1 -1
  8. package/dist/containers.module-BmICKsOK.js +5 -0
  9. package/dist/containers.module-BmICKsOK.js.map +1 -0
  10. package/dist/draggable.css +1 -1
  11. package/dist/draggable.module-DFYR5n3n.js +5 -0
  12. package/dist/draggable.module-DFYR5n3n.js.map +1 -0
  13. package/dist/field_set.css +1 -1
  14. package/dist/field_set.module-BpJTFCi4.js +5 -0
  15. package/dist/field_set.module-BpJTFCi4.js.map +1 -0
  16. package/dist/form/field/field.js +17 -13
  17. package/dist/form/field/field.js.map +1 -1
  18. package/dist/form/field_set/field_set.d.ts +6 -10
  19. package/dist/form/field_set/field_set.d.ts.map +1 -1
  20. package/dist/form/field_set/field_set.js +33 -61
  21. package/dist/form/field_set/field_set.js.map +1 -1
  22. package/dist/form/field_set/legend.d.ts +20 -0
  23. package/dist/form/field_set/legend.d.ts.map +1 -0
  24. package/dist/form/field_set/legend.js +28 -0
  25. package/dist/form/field_set/legend.js.map +1 -0
  26. package/dist/form/index.d.ts +2 -1
  27. package/dist/form/index.d.ts.map +1 -1
  28. package/dist/form/index.js +24 -22
  29. package/dist/form/index.js.map +1 -1
  30. package/dist/inputs/color_input/color_input.js +2 -3
  31. package/dist/inputs/color_input/color_input.js.map +1 -1
  32. package/dist/inputs/color_input/color_picker.js +11 -7
  33. package/dist/inputs/color_input/color_picker.js.map +1 -1
  34. package/dist/inputs/combo_box/combo_box.js +24 -20
  35. package/dist/inputs/combo_box/combo_box.js.map +1 -1
  36. package/dist/inputs/date_picker/date_picker.js +19 -15
  37. package/dist/inputs/date_picker/date_picker.js.map +1 -1
  38. package/dist/inputs/date_picker/date_picker_header.d.ts.map +1 -1
  39. package/dist/inputs/date_picker/date_picker_header.js +15 -14
  40. package/dist/inputs/date_picker/date_picker_header.js.map +1 -1
  41. package/dist/inputs/date_picker/date_picker_input.js +26 -23
  42. package/dist/inputs/date_picker/date_picker_input.js.map +1 -1
  43. package/dist/inputs/date_picker/date_picker_time_selector.js +2 -3
  44. package/dist/inputs/date_picker/date_picker_time_selector.js.map +1 -1
  45. package/dist/inputs/date_picker/date_picker_year_input.js +2 -3
  46. package/dist/inputs/date_picker/date_picker_year_input.js.map +1 -1
  47. package/dist/inputs/date_picker/date_picker_year_selector.js +24 -21
  48. package/dist/inputs/date_picker/date_picker_year_selector.js.map +1 -1
  49. package/dist/inputs/mask_input/key_capture_input.js +35 -31
  50. package/dist/inputs/mask_input/key_capture_input.js.map +1 -1
  51. package/dist/inputs/mask_input/mask_input.js +18 -14
  52. package/dist/inputs/mask_input/mask_input.js.map +1 -1
  53. package/dist/inputs/multiselect/multiselect.js +28 -24
  54. package/dist/inputs/multiselect/multiselect.js.map +1 -1
  55. package/dist/inputs/multiselect/multiselect_inline_values.d.ts.map +1 -1
  56. package/dist/inputs/multiselect/multiselect_inline_values.js +15 -15
  57. package/dist/inputs/multiselect/multiselect_inline_values.js.map +1 -1
  58. package/dist/inputs/multiselect/multiselect_values.js +16 -17
  59. package/dist/inputs/multiselect/multiselect_values.js.map +1 -1
  60. package/dist/inputs/phone_number_input/phone_number_context.js +13 -9
  61. package/dist/inputs/phone_number_input/phone_number_context.js.map +1 -1
  62. package/dist/inputs/phone_number_input/phone_number_input_adapter.js +2 -3
  63. package/dist/inputs/phone_number_input/phone_number_input_adapter.js.map +1 -1
  64. package/dist/inputs/phone_number_input/sip_input.js +8 -9
  65. package/dist/inputs/phone_number_input/sip_input.js.map +1 -1
  66. package/dist/inputs/select/select.js +11 -8
  67. package/dist/inputs/select/select.js.map +1 -1
  68. package/dist/inputs/slider/slider.js +28 -24
  69. package/dist/inputs/slider/slider.js.map +1 -1
  70. package/dist/inputs/suggestions/suggestion_list.js +11 -8
  71. package/dist/inputs/suggestions/suggestion_list.js.map +1 -1
  72. package/dist/inputs/switch/switch.js +29 -25
  73. package/dist/inputs/switch/switch.js.map +1 -1
  74. package/dist/inputs/unit_input/unit_input.js +21 -17
  75. package/dist/inputs/unit_input/unit_input.js.map +1 -1
  76. package/dist/layouts/containers/columns/columns.d.ts +6 -1
  77. package/dist/layouts/containers/columns/columns.d.ts.map +1 -1
  78. package/dist/layouts/containers/columns/columns.js +30 -7
  79. package/dist/layouts/containers/columns/columns.js.map +1 -1
  80. package/dist/layouts/containers/rail.d.ts +2 -5
  81. package/dist/layouts/containers/rail.d.ts.map +1 -1
  82. package/dist/layouts/containers/rail.js +17 -55
  83. package/dist/layouts/containers/rail.js.map +1 -1
  84. package/dist/layouts/containers/rows/index.d.ts +3 -0
  85. package/dist/layouts/containers/rows/index.d.ts.map +1 -0
  86. package/dist/layouts/containers/rows/index.js +7 -0
  87. package/dist/layouts/containers/rows/index.js.map +1 -0
  88. package/dist/layouts/containers/rows/row.d.ts +6 -0
  89. package/dist/layouts/containers/rows/row.d.ts.map +1 -0
  90. package/dist/layouts/containers/rows/row.js +20 -0
  91. package/dist/layouts/containers/rows/row.js.map +1 -0
  92. package/dist/layouts/containers/rows/rows.d.ts +11 -0
  93. package/dist/layouts/containers/rows/rows.d.ts.map +1 -0
  94. package/dist/layouts/containers/rows/rows.js +34 -0
  95. package/dist/layouts/containers/rows/rows.js.map +1 -0
  96. package/dist/layouts/containers/scaffold.d.ts +2 -5
  97. package/dist/layouts/containers/scaffold.d.ts.map +1 -1
  98. package/dist/layouts/containers/scaffold.js +17 -55
  99. package/dist/layouts/containers/scaffold.js.map +1 -1
  100. package/dist/layouts/index.d.ts +2 -0
  101. package/dist/layouts/index.d.ts.map +1 -1
  102. package/dist/layouts/index.js +26 -22
  103. package/dist/layouts/index.js.map +1 -1
  104. package/dist/mobile/inputs/date_picker/mobile_date_picker_header.js +11 -7
  105. package/dist/mobile/inputs/date_picker/mobile_date_picker_header.js.map +1 -1
  106. package/dist/mobile/inputs/date_picker/mobile_date_picker_input.js +11 -7
  107. package/dist/mobile/inputs/date_picker/mobile_date_picker_input.js.map +1 -1
  108. package/dist/mobile/inputs/date_picker/mobile_date_picker_year_selector.js +14 -10
  109. package/dist/mobile/inputs/date_picker/mobile_date_picker_year_selector.js.map +1 -1
  110. package/dist/multiselect_values.css +1 -1
  111. package/dist/navigation/tabs/state/link/tab_link.js +15 -11
  112. package/dist/navigation/tabs/state/link/tab_link.js.map +1 -1
  113. package/dist/overlay/frame/frame.d.ts.map +1 -1
  114. package/dist/overlay/frame/frame.js +117 -76
  115. package/dist/overlay/frame/frame.js.map +1 -1
  116. package/dist/overlay/menu/menu.js +21 -17
  117. package/dist/overlay/menu/menu.js.map +1 -1
  118. package/dist/overlay/popper/base/dismissal_decorator.js +3 -3
  119. package/dist/overlay/popper/base/dismissal_decorator.js.map +1 -1
  120. package/dist/overlay/popper/context_popper.js +14 -10
  121. package/dist/overlay/popper/context_popper.js.map +1 -1
  122. package/dist/overlay/popper/element_popper.js +15 -11
  123. package/dist/overlay/popper/element_popper.js.map +1 -1
  124. package/dist/overlay/popper/legacy/popper.js +32 -28
  125. package/dist/overlay/popper/legacy/popper.js.map +1 -1
  126. package/dist/overlay/popper/preview_popper.js +16 -12
  127. package/dist/overlay/popper/preview_popper.js.map +1 -1
  128. package/dist/overlay/tethered/tethered.js +17 -13
  129. package/dist/overlay/tethered/tethered.js.map +1 -1
  130. package/dist/resizable.css +1 -0
  131. package/dist/resizable.module-ur5FBfxo.js +5 -0
  132. package/dist/resizable.module-ur5FBfxo.js.map +1 -0
  133. package/dist/resize_handle.css +1 -0
  134. package/dist/row.css +1 -0
  135. package/dist/stacks/box/box.d.ts +14 -0
  136. package/dist/stacks/box/box.d.ts.map +1 -1
  137. package/dist/stacks/box/box.js +104 -102
  138. package/dist/stacks/box/box.js.map +1 -1
  139. package/dist/stacks/box/detect_resize_bounds.d.ts +1 -0
  140. package/dist/stacks/box/detect_resize_bounds.d.ts.map +1 -1
  141. package/dist/stacks/box/detect_resize_bounds.js +22 -20
  142. package/dist/stacks/box/detect_resize_bounds.js.map +1 -1
  143. package/dist/stacks/box/end_resize_handle.js +5 -5
  144. package/dist/stacks/box/end_resize_handle.js.map +1 -1
  145. package/dist/stacks/box/resize_handlers.d.ts.map +1 -1
  146. package/dist/stacks/box/resize_handlers.js +12 -12
  147. package/dist/stacks/box/resize_handlers.js.map +1 -1
  148. package/dist/stacks/box/start_resize_handle.js +7 -7
  149. package/dist/stacks/box/start_resize_handle.js.map +1 -1
  150. package/dist/stacks/box/types.d.ts +3 -2
  151. package/dist/stacks/box/types.d.ts.map +1 -1
  152. package/dist/stacks/h_collapsible_box.js +24 -20
  153. package/dist/stacks/h_collapsible_box.js.map +1 -1
  154. package/dist/stacks/v_collapsible_box.js +26 -22
  155. package/dist/stacks/v_collapsible_box.js.map +1 -1
  156. package/dist/surfaces/alert/alert.js +7 -8
  157. package/dist/surfaces/alert/alert.js.map +1 -1
  158. package/dist/surfaces/card/card.d.ts.map +1 -1
  159. package/dist/surfaces/card/card.js +14 -6
  160. package/dist/surfaces/card/card.js.map +1 -1
  161. package/dist/surfaces/pop_confirm/pop_confirm.js +4 -2
  162. package/dist/surfaces/pop_confirm/pop_confirm.js.map +1 -1
  163. package/dist/test-setup.d.ts +2 -0
  164. package/dist/test-setup.d.ts.map +1 -0
  165. package/dist/test-setup.js +10 -0
  166. package/dist/test-setup.js.map +1 -0
  167. package/dist/themes/stories/button_showcase.d.ts.map +1 -1
  168. package/dist/themes/stories/controls_fieldset.d.ts.map +1 -1
  169. package/dist/themes/stories/menu_showcase.d.ts.map +1 -1
  170. package/dist/themes/theme.d.ts.map +1 -1
  171. package/dist/themes/theme.js +17 -22
  172. package/dist/themes/theme.js.map +1 -1
  173. package/dist/themes/themes/ergo/ergo_theme.css +1 -1
  174. package/dist/themes/themes/ergo/ergo_theme.d.ts.map +1 -1
  175. package/dist/themes/themes/ergo/ergo_theme.js +653 -431
  176. package/dist/themes/themes/ergo/ergo_theme.js.map +1 -1
  177. package/dist/themes/themes/ergo/parts/actions.css +1 -0
  178. package/dist/themes/themes/ergo/parts/base.css +1 -0
  179. package/dist/themes/themes/ergo/parts/form.css +1 -0
  180. package/dist/themes/themes/ergo/parts/inputs.css +1 -0
  181. package/dist/themes/themes/ergo/parts/navigation.css +1 -0
  182. package/dist/themes/themes/windows_98/windows_98.css +1 -1
  183. package/dist/themes/themes/windows_98/windows_98_theme.js +32 -43
  184. package/dist/themes/themes/windows_98/windows_98_theme.js.map +1 -1
  185. package/dist/utils/decorators/clone_with_decorator.d.ts +21 -0
  186. package/dist/utils/decorators/clone_with_decorator.d.ts.map +1 -0
  187. package/dist/utils/decorators/clone_with_decorator.js +16 -0
  188. package/dist/utils/decorators/clone_with_decorator.js.map +1 -0
  189. package/dist/utils/decorators/draggable/context.d.ts.map +1 -0
  190. package/dist/utils/decorators/draggable/context.js.map +1 -0
  191. package/dist/utils/{dnd/handle.d.ts → decorators/draggable/drag_handle.d.ts} +1 -1
  192. package/dist/utils/decorators/draggable/drag_handle.d.ts.map +1 -0
  193. package/dist/utils/{dnd/handle.js → decorators/draggable/drag_handle.js} +2 -2
  194. package/dist/utils/decorators/draggable/drag_handle.js.map +1 -0
  195. package/dist/utils/decorators/draggable/draggable.d.ts.map +1 -0
  196. package/dist/utils/{dnd → decorators}/draggable/draggable.js +3 -3
  197. package/dist/utils/decorators/draggable/draggable.js.map +1 -0
  198. package/dist/utils/decorators/draggable/index.d.ts +11 -0
  199. package/dist/utils/decorators/draggable/index.d.ts.map +1 -0
  200. package/dist/utils/decorators/draggable/index.js +14 -0
  201. package/dist/utils/decorators/draggable/index.js.map +1 -0
  202. package/dist/utils/{dnd → decorators/draggable}/types.d.ts +1 -1
  203. package/dist/utils/decorators/draggable/types.d.ts.map +1 -0
  204. package/dist/utils/{dnd/hooks → decorators/draggable}/use_drag_container.d.ts +2 -2
  205. package/dist/utils/decorators/draggable/use_drag_container.d.ts.map +1 -0
  206. package/dist/utils/decorators/draggable/use_drag_container.js.map +1 -0
  207. package/dist/utils/decorators/draggable/use_draggable.d.ts.map +1 -0
  208. package/dist/utils/decorators/draggable/use_draggable.js.map +1 -0
  209. package/dist/utils/decorators/index.d.ts +3 -0
  210. package/dist/utils/decorators/index.d.ts.map +1 -0
  211. package/dist/utils/decorators/index.js +27 -0
  212. package/dist/utils/decorators/index.js.map +1 -0
  213. package/dist/utils/decorators/resizable/context.d.ts +4 -0
  214. package/dist/utils/decorators/resizable/context.d.ts.map +1 -0
  215. package/dist/utils/decorators/resizable/context.js +10 -0
  216. package/dist/utils/decorators/resizable/context.js.map +1 -0
  217. package/dist/utils/decorators/resizable/handle_config.d.ts +32 -0
  218. package/dist/utils/decorators/resizable/handle_config.d.ts.map +1 -0
  219. package/dist/utils/decorators/resizable/handle_config.js +62 -0
  220. package/dist/utils/decorators/resizable/handle_config.js.map +1 -0
  221. package/dist/utils/decorators/resizable/index.d.ts +10 -0
  222. package/dist/utils/decorators/resizable/index.d.ts.map +1 -0
  223. package/dist/utils/decorators/resizable/index.js +16 -0
  224. package/dist/utils/decorators/resizable/index.js.map +1 -0
  225. package/dist/utils/decorators/resizable/resizable.d.ts +11 -0
  226. package/dist/utils/decorators/resizable/resizable.d.ts.map +1 -0
  227. package/dist/utils/decorators/resizable/resizable.js +52 -0
  228. package/dist/utils/decorators/resizable/resizable.js.map +1 -0
  229. package/dist/utils/decorators/resizable/resize_handle.d.ts +7 -0
  230. package/dist/utils/decorators/resizable/resize_handle.d.ts.map +1 -0
  231. package/dist/utils/decorators/resizable/resize_handle.js +100 -0
  232. package/dist/utils/decorators/resizable/resize_handle.js.map +1 -0
  233. package/dist/utils/decorators/resizable/resize_strategy.d.ts +47 -0
  234. package/dist/utils/decorators/resizable/resize_strategy.d.ts.map +1 -0
  235. package/dist/utils/decorators/resizable/resize_strategy.js +108 -0
  236. package/dist/utils/decorators/resizable/resize_strategy.js.map +1 -0
  237. package/dist/utils/decorators/resizable/types.d.ts +28 -0
  238. package/dist/utils/decorators/resizable/types.d.ts.map +1 -0
  239. package/dist/utils/decorators/resizable/types.js +2 -0
  240. package/dist/utils/decorators/resizable/types.js.map +1 -0
  241. package/dist/utils/hooks/labelled_by_context.d.ts +21 -0
  242. package/dist/utils/hooks/labelled_by_context.d.ts.map +1 -0
  243. package/dist/utils/hooks/labelled_by_context.js +12 -0
  244. package/dist/utils/hooks/labelled_by_context.js.map +1 -0
  245. package/dist/utils/index.d.ts +8 -7
  246. package/dist/utils/index.d.ts.map +1 -1
  247. package/dist/utils/index.js +45 -28
  248. package/dist/utils/index.js.map +1 -1
  249. package/dist/utils/listeners/click_away_listener.d.ts.map +1 -0
  250. package/dist/utils/{click_away_listener.js → listeners/click_away_listener.js} +1 -1
  251. package/dist/utils/listeners/click_away_listener.js.map +1 -0
  252. package/dist/utils/listeners/focus_redirect.d.ts.map +1 -0
  253. package/dist/utils/listeners/focus_redirect.js.map +1 -0
  254. package/dist/utils/listeners/index.d.ts +4 -0
  255. package/dist/utils/listeners/index.d.ts.map +1 -0
  256. package/dist/utils/listeners/index.js +10 -0
  257. package/dist/utils/listeners/index.js.map +1 -0
  258. package/dist/utils/listeners/mouse_leave_region.d.ts.map +1 -0
  259. package/dist/utils/listeners/mouse_leave_region.js.map +1 -0
  260. package/dist/utils/listeners/scroll_away_listener.d.ts.map +1 -0
  261. package/dist/utils/{scroll_away_listener.js → listeners/scroll_away_listener.js} +1 -1
  262. package/dist/utils/listeners/scroll_away_listener.js.map +1 -0
  263. package/dist/utils/system/index.d.ts +2 -0
  264. package/dist/utils/system/index.d.ts.map +1 -0
  265. package/dist/utils/system/index.js +2 -0
  266. package/dist/utils/system/index.js.map +1 -0
  267. package/dist/utils/system/variations.d.ts.map +1 -0
  268. package/dist/utils/system/variations.js +2 -0
  269. package/dist/utils/system/variations.js.map +1 -0
  270. package/dist/utils/types/sides.d.ts +3 -0
  271. package/dist/utils/types/sides.d.ts.map +1 -0
  272. package/package.json +3 -9
  273. package/src/actions/button/__stories__/button_group.stories.tsx +23 -24
  274. package/src/actions/index.ts +0 -1
  275. package/src/form/field/field.stories.tsx +2 -2
  276. package/src/form/field/h_field/h_field.stories.tsx +1 -1
  277. package/src/form/field/v_field/v_field.stories.tsx +1 -1
  278. package/src/form/field_set/field_set.module.css +0 -14
  279. package/src/form/field_set/field_set.stories.tsx +101 -1
  280. package/src/form/field_set/field_set.tsx +43 -57
  281. package/src/form/field_set/legend.tsx +44 -0
  282. package/src/form/index.ts +6 -1
  283. package/src/inputs/date_picker/date_picker_header.tsx +7 -5
  284. package/src/inputs/date_picker/date_picker_year_selector.tsx +5 -5
  285. package/src/inputs/multiselect/multiselect_inline_values.tsx +4 -3
  286. package/src/inputs/multiselect/multiselect_values.module.css +1 -0
  287. package/src/inputs/multiselect/multiselect_values.tsx +4 -4
  288. package/src/layouts/__stories__/columns.stories.tsx +31 -0
  289. package/src/layouts/__stories__/composed.stories.tsx +77 -8
  290. package/src/layouts/__stories__/rows.stories.tsx +77 -0
  291. package/src/layouts/__stories__/utils.tsx +2 -84
  292. package/src/layouts/containers/columns/column.module.css +3 -2
  293. package/src/layouts/containers/columns/columns.tsx +29 -3
  294. package/src/layouts/containers/containers.module.css +27 -29
  295. package/src/layouts/containers/rail.tsx +9 -51
  296. package/src/layouts/containers/rows/index.ts +2 -0
  297. package/src/layouts/containers/rows/row.module.css +15 -0
  298. package/src/layouts/containers/rows/row.tsx +22 -0
  299. package/src/layouts/containers/rows/rows.tsx +42 -0
  300. package/src/layouts/containers/scaffold.tsx +9 -49
  301. package/src/layouts/index.ts +2 -0
  302. package/src/overlay/frame/frame.stories.tsx +2 -1
  303. package/src/overlay/frame/frame.tsx +68 -20
  304. package/src/overlay/popper/base/dismissal_decorator.tsx +3 -3
  305. package/src/overlay/slide/slide.stories.tsx +1 -1
  306. package/src/stacks/box/box.tsx +29 -4
  307. package/src/stacks/box/detect_resize_bounds.ts +5 -1
  308. package/src/stacks/box/end_resize_handle.tsx +1 -1
  309. package/src/stacks/box/resize_handlers.ts +1 -1
  310. package/src/stacks/box/start_resize_handle.tsx +1 -1
  311. package/src/stacks/box/types.ts +3 -2
  312. package/src/stacks/collapsible_box.stories.tsx +5 -5
  313. package/src/stacks/demo.stories.tsx +7 -7
  314. package/src/surfaces/card/card.module.css +5 -0
  315. package/src/surfaces/card/card.stories.tsx +66 -8
  316. package/src/surfaces/card/card.tsx +6 -2
  317. package/src/surfaces/page/page.stories.tsx +84 -4
  318. package/src/surfaces/panel/__stories__/panel.stories.tsx +84 -9
  319. package/src/surfaces/window/window.stories.tsx +1 -1
  320. package/src/test-setup.ts +11 -0
  321. package/src/themes/stories/button_showcase.tsx +3 -1
  322. package/src/themes/stories/controls_fieldset.tsx +3 -1
  323. package/src/themes/stories/menu_showcase.tsx +3 -1
  324. package/src/themes/theme.tsx +6 -16
  325. package/src/themes/themes/ergo/INTERACTIVE.md +89 -0
  326. package/src/themes/themes/ergo/ROADMAP.md +116 -0
  327. package/src/themes/themes/ergo/ergo_theme.css +219 -734
  328. package/src/themes/themes/ergo/ergo_theme.ts +15 -1
  329. package/src/themes/themes/ergo/parts/actions.css +287 -0
  330. package/src/themes/themes/ergo/parts/base.css +62 -0
  331. package/src/themes/themes/ergo/parts/form.css +23 -0
  332. package/src/themes/themes/ergo/parts/inputs.css +252 -0
  333. package/src/themes/themes/ergo/parts/navigation.css +104 -0
  334. package/src/themes/themes/windows_98/windows_98.css +32 -43
  335. package/src/tokens/chip/chip.stories.tsx +5 -5
  336. package/src/utils/decorators/DECORATOR_PATTERN.md +86 -0
  337. package/src/utils/decorators/clone_with_decorator.ts +47 -0
  338. package/src/utils/{dnd → decorators/draggable}/__stories__/draggable.stories.tsx +7 -7
  339. package/src/utils/{dnd → decorators/draggable}/__stories__/use_draggable.stories.tsx +2 -2
  340. package/src/utils/{dnd/handle.tsx → decorators/draggable/drag_handle.tsx} +1 -1
  341. package/src/utils/{dnd → decorators}/draggable/draggable.tsx +2 -2
  342. package/src/utils/decorators/draggable/index.ts +15 -0
  343. package/src/utils/{dnd → decorators/draggable}/types.ts +1 -1
  344. package/src/utils/{dnd/hooks → decorators/draggable}/use_drag_container.ts +2 -2
  345. package/src/utils/decorators/index.ts +2 -0
  346. package/src/utils/decorators/resizable/__stories__/resizable.stories.tsx +214 -0
  347. package/src/utils/decorators/resizable/__stories__/resizable_stories.module.css +47 -0
  348. package/src/utils/decorators/resizable/__tests__/handle_config.test.ts +191 -0
  349. package/src/utils/decorators/resizable/__tests__/resize_strategy.test.ts +163 -0
  350. package/src/utils/decorators/resizable/context.ts +9 -0
  351. package/src/utils/decorators/resizable/handle_config.ts +118 -0
  352. package/src/utils/decorators/resizable/index.ts +37 -0
  353. package/src/utils/decorators/resizable/resizable.module.css +5 -0
  354. package/src/utils/decorators/resizable/resizable.tsx +97 -0
  355. package/src/utils/decorators/resizable/resize_handle.module.css +106 -0
  356. package/src/utils/decorators/resizable/resize_handle.tsx +165 -0
  357. package/src/utils/decorators/resizable/resize_strategy.ts +190 -0
  358. package/src/utils/decorators/resizable/types.ts +58 -0
  359. package/src/utils/hooks/labelled_by_context.ts +27 -0
  360. package/src/utils/index.ts +9 -7
  361. package/src/utils/{click_away_listener.tsx → listeners/click_away_listener.tsx} +1 -1
  362. package/src/utils/listeners/index.ts +3 -0
  363. package/src/utils/{scroll_away_listener.tsx → listeners/scroll_away_listener.tsx} +1 -1
  364. package/src/utils/system/index.ts +1 -0
  365. package/src/utils/types/sides.ts +2 -0
  366. package/dist/actions/button/slim_button/slim_button.d.ts +0 -9
  367. package/dist/actions/button/slim_button/slim_button.d.ts.map +0 -1
  368. package/dist/actions/button/slim_button/slim_button.js +0 -18
  369. package/dist/actions/button/slim_button/slim_button.js.map +0 -1
  370. package/dist/containers.module-DlGySre0.js +0 -5
  371. package/dist/containers.module-DlGySre0.js.map +0 -1
  372. package/dist/draggable.module-BgelQsuJ.js +0 -5
  373. package/dist/draggable.module-BgelQsuJ.js.map +0 -1
  374. package/dist/frame.css +0 -1
  375. package/dist/left_resize_handle.css +0 -1
  376. package/dist/right_resize_handle.css +0 -1
  377. package/dist/slim_button.css +0 -1
  378. package/dist/stacks/box/left_resize_handle.d.ts +0 -4
  379. package/dist/stacks/box/left_resize_handle.d.ts.map +0 -1
  380. package/dist/stacks/box/left_resize_handle.js +0 -36
  381. package/dist/stacks/box/left_resize_handle.js.map +0 -1
  382. package/dist/stacks/box/right_resize_handle.d.ts +0 -4
  383. package/dist/stacks/box/right_resize_handle.d.ts.map +0 -1
  384. package/dist/stacks/box/right_resize_handle.js +0 -36
  385. package/dist/stacks/box/right_resize_handle.js.map +0 -1
  386. package/dist/utils/click_away_listener.d.ts.map +0 -1
  387. package/dist/utils/click_away_listener.js.map +0 -1
  388. package/dist/utils/dnd/context.d.ts.map +0 -1
  389. package/dist/utils/dnd/context.js.map +0 -1
  390. package/dist/utils/dnd/draggable/draggable.d.ts.map +0 -1
  391. package/dist/utils/dnd/draggable/draggable.js.map +0 -1
  392. package/dist/utils/dnd/handle.d.ts.map +0 -1
  393. package/dist/utils/dnd/handle.js.map +0 -1
  394. package/dist/utils/dnd/hooks/use_drag_container.d.ts.map +0 -1
  395. package/dist/utils/dnd/hooks/use_drag_container.js.map +0 -1
  396. package/dist/utils/dnd/hooks/use_draggable.d.ts.map +0 -1
  397. package/dist/utils/dnd/hooks/use_draggable.js.map +0 -1
  398. package/dist/utils/dnd/types.d.ts.map +0 -1
  399. package/dist/utils/focus_redirect.d.ts.map +0 -1
  400. package/dist/utils/focus_redirect.js.map +0 -1
  401. package/dist/utils/mouse_leave_region.d.ts.map +0 -1
  402. package/dist/utils/mouse_leave_region.js.map +0 -1
  403. package/dist/utils/scroll_away_listener.d.ts.map +0 -1
  404. package/dist/utils/scroll_away_listener.js.map +0 -1
  405. package/dist/utils/types/variations.d.ts.map +0 -1
  406. package/src/actions/button/__stories__/slim_button.stories.tsx +0 -274
  407. package/src/actions/button/slim_button/slim_button.module.css +0 -9
  408. package/src/actions/button/slim_button/slim_button.tsx +0 -26
  409. package/src/overlay/frame/frame.module.css +0 -5
  410. package/src/stacks/box/left_resize_handle.module.css +0 -12
  411. package/src/stacks/box/left_resize_handle.tsx +0 -39
  412. package/src/stacks/box/right_resize_handle.module.css +0 -12
  413. package/src/stacks/box/right_resize_handle.tsx +0 -38
  414. /package/dist/utils/{dnd → decorators/draggable}/context.d.ts +0 -0
  415. /package/dist/utils/{dnd → decorators/draggable}/context.js +0 -0
  416. /package/dist/utils/{dnd → decorators}/draggable/draggable.d.ts +0 -0
  417. /package/dist/utils/{dnd → decorators/draggable}/types.js +0 -0
  418. /package/dist/utils/{dnd → decorators/draggable}/types.js.map +0 -0
  419. /package/dist/utils/{dnd/hooks → decorators/draggable}/use_drag_container.js +0 -0
  420. /package/dist/utils/{dnd/hooks → decorators/draggable}/use_draggable.d.ts +0 -0
  421. /package/dist/utils/{dnd/hooks → decorators/draggable}/use_draggable.js +0 -0
  422. /package/dist/utils/{click_away_listener.d.ts → listeners/click_away_listener.d.ts} +0 -0
  423. /package/dist/utils/{focus_redirect.d.ts → listeners/focus_redirect.d.ts} +0 -0
  424. /package/dist/utils/{focus_redirect.js → listeners/focus_redirect.js} +0 -0
  425. /package/dist/utils/{mouse_leave_region.d.ts → listeners/mouse_leave_region.d.ts} +0 -0
  426. /package/dist/utils/{mouse_leave_region.js → listeners/mouse_leave_region.js} +0 -0
  427. /package/dist/utils/{scroll_away_listener.d.ts → listeners/scroll_away_listener.d.ts} +0 -0
  428. /package/dist/utils/{types → system}/variations.d.ts +0 -0
  429. /package/src/utils/{dnd → decorators/draggable}/__stories__/draggable_stories.module.css +0 -0
  430. /package/src/utils/{dnd → decorators/draggable}/context.ts +0 -0
  431. /package/src/utils/{dnd → decorators}/draggable/draggable.module.css +0 -0
  432. /package/src/utils/{dnd/hooks → decorators/draggable}/use_draggable.ts +0 -0
  433. /package/src/utils/{click_away_listener.stories.tsx → listeners/click_away_listener.stories.tsx} +0 -0
  434. /package/src/utils/{focus_redirect.tsx → listeners/focus_redirect.tsx} +0 -0
  435. /package/src/utils/{mouse_leave_region.tsx → listeners/mouse_leave_region.tsx} +0 -0
  436. /package/src/utils/{scroll_away_listener.stories.tsx → listeners/scroll_away_listener.stories.tsx} +0 -0
  437. /package/src/utils/{types → system}/variations.ts +0 -0
@@ -0,0 +1,104 @@
1
+ @layer tcn-theme {
2
+ /* ===== Tabs ===== */
3
+ .tcn-tabs-bar {
4
+ .tcn-tabs-list {
5
+ .tcn-tab-item {
6
+ min-height: 24px;
7
+ padding: 0px var(--padding-medium);
8
+ text-decoration: none;
9
+ text-overflow: ellipsis;
10
+ overflow: hidden;
11
+ white-space: nowrap;
12
+ }
13
+ }
14
+ }
15
+
16
+ /* Default */
17
+ .tcn-tabs-bar[data-variant="default"] {
18
+ .tcn-tabs-list {
19
+ .tcn-tab-item {
20
+ box-sizing: border-box;
21
+ border: none;
22
+ padding: 0px var(--padding-medium);
23
+ border-radius: 0;
24
+ }
25
+ .tcn-tab-item[data-is-selected="true"] {
26
+ --mat: var(--tcn-button-color, var(--material));
27
+ --ink: var(--tcn-button-text-color, var(--ergo-primary));
28
+ --act: var(--ergo-primary);
29
+ }
30
+
31
+ .tcn-tab-item:focus-visible {
32
+ z-index: 2;
33
+ }
34
+ /* Hover Indicator */
35
+ .tcn-tab-item[data-is-selected="false"]:hover::after,
36
+ .tcn-tab-item[data-is-selected="false"]:focus-visible::after {
37
+ content: "";
38
+ display: block;
39
+ position: absolute;
40
+ left: 0;
41
+ right: 0;
42
+ bottom: 0px;
43
+ height: 1px;
44
+ background: var(--ergo-accent-blue);
45
+ pointer-events: none;
46
+ width: 100%;
47
+ z-index: 3;
48
+ }
49
+
50
+ /* Indicator */
51
+ &::after {
52
+ content: "";
53
+ position: absolute;
54
+ bottom: -1px;
55
+ left: 0;
56
+ min-height: 2px;
57
+ transform: translateX(var(--tabs-active-rectangle-position-x, 0));
58
+ width: var(--tabs-active-rectangle-width, 0);
59
+ background: var(--ergo-primary);
60
+ pointer-events: none;
61
+ border-bottom-left-radius: 2px;
62
+ border-bottom-right-radius: 2px;
63
+ transition:
64
+ transform 300ms ease-in-out,
65
+ width 300ms ease-in-out;
66
+ will-change: transform, width;
67
+ z-index: 2;
68
+ }
69
+ }
70
+
71
+ /* Rail */
72
+ &::before {
73
+ content: "";
74
+ position: absolute;
75
+ bottom: 0px;
76
+ left: 0;
77
+ width: 100%;
78
+ height: 1px;
79
+ background: var(--ergo-material-divider);
80
+ pointer-events: none;
81
+ z-index: 1;
82
+ }
83
+ }
84
+
85
+ /* Inline */
86
+ .tcn-tabs-bar[data-variant="inline"] {
87
+ font-size: 12px;
88
+ min-width: min-content;
89
+ width: auto;
90
+ flex-grow: 0;
91
+ border-radius: 6px;
92
+ border: 1px solid var(--on-material);
93
+ padding: 2px;
94
+
95
+ .tcn-tabs-list {
96
+ height: 20px;
97
+ gap: var(--ergo-spacing-xs);
98
+ .tcn-tab-item {
99
+ border-radius: var(--ergo-shape-radius-medium);
100
+ min-height: 20px;
101
+ }
102
+ }
103
+ }
104
+ }
@@ -112,6 +112,37 @@
112
112
  outline: none;
113
113
  }
114
114
 
115
+ /* Utility button styles — selectors mirror hierarchy/hover combos above to win specificity */
116
+ .tcn-button[data-is-utility="true"],
117
+ .tcn-button[data-is-utility="true"][data-hierarchy="primary"],
118
+ .tcn-button[data-is-utility="true"][data-hierarchy="secondary"],
119
+ .tcn-button[data-is-utility="true"][data-hierarchy="primary"]:hover,
120
+ .tcn-button[data-is-utility="true"][data-hierarchy="secondary"]:hover {
121
+ padding: 2px;
122
+ min-width: 0;
123
+ min-height: 0;
124
+ height: auto;
125
+ width: auto;
126
+ }
127
+
128
+ .tcn-button[data-is-utility="true"][data-size="sm"],
129
+ .tcn-button[data-is-utility="true"][data-hierarchy="primary"][data-size="sm"]:hover,
130
+ .tcn-button[data-is-utility="true"][data-hierarchy="secondary"][data-size="sm"]:hover {
131
+ padding: 1px;
132
+ }
133
+
134
+ .tcn-button[data-is-utility="true"][data-size="md"],
135
+ .tcn-button[data-is-utility="true"][data-hierarchy="primary"][data-size="md"]:hover,
136
+ .tcn-button[data-is-utility="true"][data-hierarchy="secondary"][data-size="md"]:hover {
137
+ padding: 2px;
138
+ }
139
+
140
+ .tcn-button[data-is-utility="true"][data-size="lg"],
141
+ .tcn-button[data-is-utility="true"][data-hierarchy="primary"][data-size="lg"]:hover,
142
+ .tcn-button[data-is-utility="true"][data-hierarchy="secondary"][data-size="lg"]:hover {
143
+ padding: 4px;
144
+ }
145
+
115
146
  .tcn-button[data-is-disabled="true"] {
116
147
  text-shadow: 1px 1px 0 #fff;
117
148
  }
@@ -289,43 +320,6 @@
289
320
  pointer-events: none;
290
321
  }
291
322
 
292
- /* Slim button styles */
293
- .tcn-slim-button,
294
- .tcn-slim-button[data-hierarchy="primary"],
295
- .tcn-slim-button[data-hierarchy="secondary"],
296
- .tcn-slim-button[data-hierarchy="primary"]:hover,
297
- .tcn-slim-button[data-hierarchy="secondary"]:hover {
298
- padding: 2px;
299
- min-width: 0;
300
- min-height: 0;
301
- height: auto;
302
- width: auto;
303
- }
304
-
305
- .tcn-slim-button[data-size="sm"],
306
- .tcn-slim-button[data-hierarchy="primary"][data-size="sm"],
307
- .tcn-slim-button[data-hierarchy="secondary"][data-size="sm"],
308
- .tcn-slim-button[data-hierarchy="primary"][data-size="sm"]:hover,
309
- .tcn-slim-button[data-hierarchy="secondary"][data-size="sm"]:hover {
310
- padding: 1px;
311
- }
312
-
313
- .tcn-slim-button[data-size="md"],
314
- .tcn-slim-button[data-hierarchy="primary"][data-size="md"],
315
- .tcn-slim-button[data-hierarchy="secondary"][data-size="md"],
316
- .tcn-slim-button[data-hierarchy="primary"][data-size="md"]:hover,
317
- .tcn-slim-button[data-hierarchy="secondary"][data-size="md"]:hover {
318
- padding: 2px;
319
- }
320
-
321
- .tcn-slim-button[data-size="lg"],
322
- .tcn-slim-button[data-hierarchy="primary"][data-size="lg"],
323
- .tcn-slim-button[data-hierarchy="secondary"][data-size="lg"],
324
- .tcn-slim-button[data-hierarchy="primary"][data-size="lg"]:hover,
325
- .tcn-slim-button[data-hierarchy="secondary"][data-size="lg"]:hover {
326
- padding: 4px;
327
- }
328
-
329
323
  /* Select group styles */
330
324
  .tcn-select-group .tcn-button[data-hierarchy="primary"],
331
325
  .tcn-select-group .tcn-button[data-hierarchy="primary"]:hover,
@@ -682,6 +676,7 @@
682
676
  margin-top: 10px;
683
677
  border-radius: 0;
684
678
  background: #c0c0c0;
679
+ gap: 8px;
685
680
  box-shadow:
686
681
  inset 1px 1px #808080,
687
682
  inset -1px -1px #f5f5f5,
@@ -689,12 +684,6 @@
689
684
  inset -2px -2px #808080;
690
685
  }
691
686
 
692
- .tcn-field-set > .tcn-field-set-body {
693
- background: transparent;
694
- padding: 0;
695
- gap: 8px;
696
- }
697
-
698
687
  .tcn-field-set > .tcn-field-set-legend {
699
688
  position: relative;
700
689
  top: -7px;
@@ -1,7 +1,7 @@
1
1
  import { HStack } from '../../stacks/h_stack.js';
2
2
  import { Spacer } from '../../stacks/spacer.js';
3
3
  import React from 'react';
4
- import { SlimButton } from '../../actions/index.js';
4
+ import { Button } from '../../actions/index.js';
5
5
  import { Chip } from './chip.js';
6
6
  import { BodyText } from '../../typography/index.js';
7
7
  import { CrossIcon } from '@tcn/icons/cross_icon.js';
@@ -21,9 +21,9 @@ export const WithIcon = () => (
21
21
  <HStack>
22
22
  <BodyText padStart="8px">Hola Mundo</BodyText>
23
23
  <Spacer width="8px" />
24
- <SlimButton size="sm" hierarchy="tertiary">
24
+ <Button utility size="sm" hierarchy="tertiary">
25
25
  <CrossIcon size="sm" />
26
- </SlimButton>
26
+ </Button>
27
27
  </HStack>
28
28
  </Chip>
29
29
  );
@@ -40,9 +40,9 @@ export const AllVariations = () => (
40
40
  // <HStack>
41
41
  // <BodyText padStart="8px">Hola Mundo</BodyText>
42
42
  // <Spacer width="8px" />
43
- // <SlimButton size="sm" hierarchy="tertiary">
43
+ // <Button utility size="sm" hierarchy="tertiary">
44
44
  // <CrossIcon size="sm" />
45
- // </SlimButton>
45
+ // </Button>
46
46
  // </HStack>
47
47
  // </Chip>
48
48
  // }
@@ -0,0 +1,86 @@
1
+ # Decorator Pattern
2
+
3
+ Decorators are components that wrap exactly one child element via `React.cloneElement`, inject behavior (props, refs, classNames, CSS variables), and provide a context for sub-components (handles, controls).
4
+
5
+ ## What Makes a Decorator
6
+
7
+ - Wraps a single child element using `cloneElement`
8
+ - Injects props onto the child without the child's knowledge
9
+ - Can provide a React context consumed by sub-components (e.g. `ResizeHandle` reads `ResizableContext`)
10
+ - Is composable by stacking: `<Draggable><Resizable><div /></Resizable></Draggable>`
11
+
12
+ Decorators are distinct from **listeners** (which monitor external events like clicks-outside or scrolls without using `cloneElement`).
13
+
14
+ ## cloneElement Rules
15
+
16
+ When cloning the child element:
17
+
18
+ 1. **Merge `className`** via `clsx` -- never replace the child's existing classes
19
+ 2. **Fork `ref`** via `useForkRef` -- preserve both the decorator's internal ref and the consumer's forwarded ref
20
+ 3. **Spread `style`** -- merge into the child's existing style, never replace
21
+ 4. **Use `tcn-*` CSS class** -- add a `tcn-<decorator-name>` class for external styling hooks
22
+ 5. **Use `data-*` attributes** for state (`data-is-dragging`, `data-is-resizable`, etc.)
23
+
24
+ The shared utility `clone_with_decorator.ts` consolidates this boilerplate.
25
+
26
+ ## Context Pattern
27
+
28
+ Each decorator provides its own context:
29
+
30
+ ```tsx
31
+ // context.ts
32
+ export const ResizableContext = createContext<ResizableContextValue | undefined>(undefined);
33
+ export const useResizable = makeContextHook(ResizableContext, 'ResizableContext');
34
+ ```
35
+
36
+ Sub-components (handles, controls) consume this context to coordinate with their parent decorator.
37
+
38
+ ## displayName Discrimination
39
+
40
+ When a decorator needs to separate its own sub-components from other children, use `displayName` matching:
41
+
42
+ ```tsx
43
+ function isResizeHandle(child: React.ReactNode): boolean {
44
+ return (type as { displayName?: string }).displayName === 'ResizeHandle';
45
+ }
46
+ ```
47
+
48
+ Always set `displayName` on sub-components. Note: wrapping a sub-component in a HOC can break `displayName` matching -- keep sub-components as direct exports.
49
+
50
+ ## Stacking Decorators
51
+
52
+ Decorators compose cleanly by stacking:
53
+
54
+ ```tsx
55
+ <Draggable>
56
+ <Resizable>
57
+ <div>content</div>
58
+ </Resizable>
59
+ </Draggable>
60
+ ```
61
+
62
+ This works because:
63
+ - Each decorator forks the ref chain independently via `useForkRef`
64
+ - Props are merged (className via clsx, style via spread), not replaced
65
+ - Each decorator provides its own isolated context
66
+
67
+ No optimization for single-`cloneElement` composition is needed -- `cloneElement` is synchronous and cheap.
68
+
69
+ ## Creating a New Decorator
70
+
71
+ Checklist:
72
+
73
+ 1. Create a directory under `utils/decorators/<name>/`
74
+ 2. Use `cloneWithDecorator()` from `clone_with_decorator.ts` for the cloneElement boilerplate
75
+ 3. (Optional) Create a context + `makeContextHook` for sub-component communication
76
+ 4. Set `displayName` on all sub-components
77
+ 5. Add `tcn-<name>` CSS class on the target element
78
+ 6. Add `data-*` attributes for observable state
79
+ 7. Write stories for solo usage and stacked usage with other decorators
80
+ 8. Export from the decorator's `index.ts` barrel
81
+ 9. Re-export from `utils/decorators/index.ts`
82
+
83
+ ## Current Decorators
84
+
85
+ - **Resizable** (`decorators/resizable/`) -- makes an element resizable via drag handles
86
+ - **Draggable** (`decorators/draggable/`) -- makes an element draggable via drag handles
@@ -0,0 +1,47 @@
1
+ import React from 'react';
2
+ import type { ClassValue } from 'clsx';
3
+ import { clsx } from 'clsx';
4
+
5
+ /**
6
+ * Shared utility for decorator-pattern components that wrap a child via cloneElement.
7
+ *
8
+ * Consolidates the boilerplate: merges className via clsx, spreads style,
9
+ * accepts a pre-forked ref (callers must invoke useForkRef themselves),
10
+ * and optionally appends extra children (e.g. handles).
11
+ *
12
+ * Internal only — not exported from the package.
13
+ */
14
+
15
+ interface DecoratorProps {
16
+ className?: ClassValue;
17
+ style?: React.CSSProperties;
18
+ ref?: React.Ref<unknown>;
19
+ children?: React.ReactNode;
20
+ [key: string]: unknown;
21
+ }
22
+
23
+ export function cloneWithDecorator(
24
+ child: React.ReactElement,
25
+ injectedProps: DecoratorProps,
26
+ extraChildren?: React.ReactNode[]
27
+ ): React.ReactElement {
28
+ const { className, style, ref, children, ...rest } = injectedProps;
29
+
30
+ const mergedProps: Record<string, unknown> = {
31
+ ...rest,
32
+ className: clsx(child.props.className, className),
33
+ style: { ...child.props.style, ...(style as object) },
34
+ };
35
+
36
+ if (ref !== undefined) {
37
+ mergedProps.ref = ref;
38
+ }
39
+
40
+ const childContent = children ?? child.props.children;
41
+
42
+ if (extraChildren && extraChildren.length > 0) {
43
+ return React.cloneElement(child, mergedProps, childContent, ...extraChildren);
44
+ }
45
+
46
+ return React.cloneElement(child, mergedProps);
47
+ }
@@ -1,11 +1,11 @@
1
- import { Button } from '../../../actions/button/button/button.js';
2
- import { Box } from '../../../stacks/box/box.js';
3
- import { VStack } from '../../../stacks/v_stack.js';
4
- import { ZStack } from '../../../stacks/z_stack.js';
5
- import { BodyText } from '../../../typography/index.js';
1
+ import { Button } from '../../../../actions/button/button/button.js';
2
+ import { Box } from '../../../../stacks/box/box.js';
3
+ import { VStack } from '../../../../stacks/v_stack.js';
4
+ import { ZStack } from '../../../../stacks/z_stack.js';
5
+ import { BodyText } from '../../../../typography/index.js';
6
6
  import { useDragContainer } from '../context.js';
7
- import { Draggable } from '../draggable/draggable.js';
8
- import { DragHandle } from '../handle.js';
7
+ import { Draggable } from '../draggable.js';
8
+ import { DragHandle } from '../drag_handle.js';
9
9
  import styles from './draggable_stories.module.css';
10
10
 
11
11
  export default {
@@ -1,6 +1,6 @@
1
1
  import React, { useState, useRef } from 'react';
2
- import { useDraggable } from '../hooks/use_draggable';
3
- import { Box } from '../../../stacks/box/box.js';
2
+ import { useDraggable } from '../use_draggable.js';
3
+ import { Box } from '../../../../stacks/box/box.js';
4
4
 
5
5
  export default {
6
6
  title: 'Utils/useDraggable',
@@ -1,7 +1,7 @@
1
1
  import React, { useEffect, useRef } from 'react';
2
2
  import clsx from 'clsx';
3
3
  import { useDragContainer } from './context.js';
4
- import { useForkRef } from '../hooks/use_fork_ref.js';
4
+ import { useForkRef } from '../../hooks/use_fork_ref.js';
5
5
 
6
6
  export interface DragHandleProps {
7
7
  children: React.ReactElement;
@@ -1,8 +1,8 @@
1
1
  import React, { useMemo, type CSSProperties } from 'react';
2
2
 
3
- import { DragContainerContext } from '../context.js';
3
+ import { DragContainerContext } from './context.js';
4
4
  import clsx from 'clsx';
5
- import { useMakeDragContainer } from '../hooks/use_drag_container.js';
5
+ import { useMakeDragContainer } from './use_drag_container.js';
6
6
 
7
7
  import styles from './draggable.module.css';
8
8
 
@@ -0,0 +1,15 @@
1
+ export { Draggable } from './draggable.js';
2
+ export type { DraggableProps } from './draggable.js';
3
+
4
+ export { DragHandle } from './drag_handle.js';
5
+ export type { DragHandleProps } from './drag_handle.js';
6
+
7
+ export { useDragContainer, DragContainerContext } from './context.js';
8
+
9
+ export { useDraggable } from './use_draggable.js';
10
+ export type { UseDraggableOptions } from './use_draggable.js';
11
+
12
+ export { useMakeDragContainer } from './use_drag_container.js';
13
+ export type { UseDragContainerOptions } from './use_drag_container.js';
14
+
15
+ export type { DragContainer } from './types.js';
@@ -1,4 +1,4 @@
1
- import type { Position } from '../types/dimensions.js';
1
+ import type { Position } from '../../types/dimensions.js';
2
2
 
3
3
  export interface DragContainer {
4
4
  registerHandle: (handle: React.RefObject<HTMLElement>) => void;
@@ -1,6 +1,6 @@
1
1
  import { useCallback, useRef, useState } from 'react';
2
- import type { Position } from '../../index.js';
3
- import type { DragContainer } from '../types.js';
2
+ import type { Position } from '../../types/dimensions.js';
3
+ import type { DragContainer } from './types.js';
4
4
  import { useDraggable } from './use_draggable.js';
5
5
 
6
6
  export interface UseDragContainerOptions {
@@ -0,0 +1,2 @@
1
+ export * from './resizable/index.js';
2
+ export * from './draggable/index.js';
@@ -0,0 +1,214 @@
1
+ import { useRef, useState } from 'react';
2
+ import { Resizable } from '../resizable.js';
3
+ import { ResizeHandle } from '../resize_handle.js';
4
+ import { Box } from '../../../../stacks/box/box.js';
5
+ import { VStack } from '../../../../stacks/v_stack.js';
6
+ import { BodyText } from '../../../../typography/index.js';
7
+ import styles from './resizable_stories.module.css';
8
+
9
+ export default {
10
+ title: 'Utils/Resizable',
11
+ component: Resizable,
12
+ tags: ['autodocs'],
13
+ };
14
+
15
+ export const HorizontalResize = () => {
16
+ const [width, setWidth] = useState(300);
17
+
18
+ return (
19
+ <VStack gap="8px" padding="16px" minHeight="400px">
20
+ <BodyText>Drag the end edge to resize horizontally.</BodyText>
21
+ <Resizable onWidthResize={({ width: w }) => setWidth(Math.round(w))}>
22
+ <Box width={`${width}px`} height="200px" className={styles['demo-box']}>
23
+ <BodyText>Resizable content</BodyText>
24
+ <div className={styles['size-display']}>width: {width}px</div>
25
+ </Box>
26
+ <ResizeHandle position="end" className={styles['demo-handle']} />
27
+ </Resizable>
28
+ </VStack>
29
+ );
30
+ };
31
+
32
+ export const VerticalResize = () => {
33
+ const [height, setHeight] = useState(200);
34
+
35
+ return (
36
+ <VStack gap="8px" padding="16px" minHeight="600px">
37
+ <BodyText>Drag the bottom edge to resize vertically.</BodyText>
38
+ <Resizable onHeightResize={({ height: h }) => setHeight(Math.round(h))}>
39
+ <Box width="400px" height={`${height}px`} className={styles['demo-box']}>
40
+ <BodyText>Resizable content</BodyText>
41
+ <div className={styles['size-display']}>height: {height}px</div>
42
+ </Box>
43
+ <ResizeHandle position="bottom" className={styles['demo-handle']} />
44
+ </Resizable>
45
+ </VStack>
46
+ );
47
+ };
48
+
49
+ export const CornerResize = () => {
50
+ const [width, setWidth] = useState(300);
51
+ const [height, setHeight] = useState(200);
52
+
53
+ return (
54
+ <VStack gap="8px" padding="16px" minHeight="600px">
55
+ <BodyText>
56
+ Drag the bottom-right corner to resize both axes simultaneously.
57
+ </BodyText>
58
+ <Resizable
59
+ onWidthResize={({ width: w }) => setWidth(Math.round(w))}
60
+ onHeightResize={({ height: h }) => setHeight(Math.round(h))}
61
+ >
62
+ <Box width={`${width}px`} height={`${height}px`} className={styles['demo-box']}>
63
+ <BodyText>Resizable content</BodyText>
64
+ <div className={styles['size-display']}>
65
+ {width}px × {height}px
66
+ </div>
67
+ </Box>
68
+ <ResizeHandle position="bottom-end" className={styles['demo-handle']} />
69
+ </Resizable>
70
+ </VStack>
71
+ );
72
+ };
73
+
74
+ export const MultipleHandles = () => {
75
+ const [width, setWidth] = useState(400);
76
+ const [height, setHeight] = useState(300);
77
+
78
+ return (
79
+ <VStack gap="8px" padding="16px" minHeight="600px">
80
+ <BodyText>Start, end, bottom edges and bottom-end corner — all active.</BodyText>
81
+ <Resizable
82
+ onWidthResize={({ width: w }) => setWidth(Math.round(w))}
83
+ onHeightResize={({ height: h }) => setHeight(Math.round(h))}
84
+ >
85
+ <Box width={`${width}px`} height={`${height}px`} className={styles['demo-box']}>
86
+ <BodyText>Resizable content</BodyText>
87
+ <div className={styles['size-display']}>
88
+ {width}px × {height}px
89
+ </div>
90
+ </Box>
91
+ <ResizeHandle position="start" className={styles['demo-handle']} />
92
+ <ResizeHandle position="end" className={styles['demo-handle']} />
93
+ <ResizeHandle position="bottom" className={styles['demo-handle']} />
94
+ <ResizeHandle
95
+ position="bottom-end"
96
+ className={styles['demo-handle']}
97
+ style={{
98
+ zIndex: 2,
99
+ }}
100
+ />
101
+ </Resizable>
102
+ </VStack>
103
+ );
104
+ };
105
+
106
+ export const RTL = () => {
107
+ const [widthStart, setWidthStart] = useState(300);
108
+ const [widthEnd, setWidthEnd] = useState(300);
109
+
110
+ return (
111
+ <VStack gap="16px" padding="16px" minHeight="400px">
112
+ <BodyText>
113
+ RTL context: &quot;start&quot; flips to the right edge, &quot;end&quot; flips to
114
+ the left edge.
115
+ </BodyText>
116
+ <div className={styles['rtl-container']}>
117
+ <VStack gap="16px">
118
+ <BodyText>position=&quot;start&quot; (flips to right edge in RTL)</BodyText>
119
+ <Resizable onWidthResize={({ width: w }) => setWidthStart(Math.round(w))}>
120
+ <Box width={`${widthStart}px`} height="100px" className={styles['demo-box']}>
121
+ <div className={styles['size-display']}>start handle: {widthStart}px</div>
122
+ </Box>
123
+ <ResizeHandle position="start" className={styles['demo-handle']} />
124
+ </Resizable>
125
+
126
+ <BodyText>position=&quot;end&quot; (flips to left edge in RTL)</BodyText>
127
+ <Resizable onWidthResize={({ width: w }) => setWidthEnd(Math.round(w))}>
128
+ <Box width={`${widthEnd}px`} height="100px" className={styles['demo-box']}>
129
+ <div className={styles['size-display']}>end handle: {widthEnd}px</div>
130
+ </Box>
131
+ <ResizeHandle position="end" className={styles['demo-handle']} />
132
+ </Resizable>
133
+ </VStack>
134
+ </div>
135
+ </VStack>
136
+ );
137
+ };
138
+
139
+ export const NestedResizable = () => {
140
+ const [outerWidth, setOuterWidth] = useState(500);
141
+ const [innerWidth, setInnerWidth] = useState(250);
142
+
143
+ return (
144
+ <VStack gap="8px" padding="16px" minHeight="400px">
145
+ <BodyText>Nested: each handle binds to its nearest Resizable provider.</BodyText>
146
+ <Resizable onWidthResize={({ width: w }) => setOuterWidth(Math.round(w))}>
147
+ <Box width={`${outerWidth}px`} height="300px" className={styles['nested-outer']}>
148
+ <BodyText>Outer ({outerWidth}px)</BodyText>
149
+ <Resizable onWidthResize={({ width: w }) => setInnerWidth(Math.round(w))}>
150
+ <Box
151
+ width={`${innerWidth}px`}
152
+ height="150px"
153
+ className={styles['nested-inner']}
154
+ >
155
+ <BodyText>Inner ({innerWidth}px)</BodyText>
156
+ </Box>
157
+ <ResizeHandle position="end" className={styles['demo-handle']} />
158
+ </Resizable>
159
+ </Box>
160
+ <ResizeHandle position="end" className={styles['demo-handle']} />
161
+ </Resizable>
162
+ </VStack>
163
+ );
164
+ };
165
+
166
+ export const OverflowHidden = () => {
167
+ const [width, setWidth] = useState(300);
168
+
169
+ return (
170
+ <VStack gap="8px" padding="16px" minHeight="400px">
171
+ <BodyText>
172
+ Outer box is the resize target (minWidth: 100px). Inner box clips content with
173
+ overflow:hidden. Shrink below 200px to clip the purple box. It stops at 100px due
174
+ to the min-width constraint.
175
+ </BodyText>
176
+ <Resizable onWidthResize={({ width: w }) => setWidth(Math.round(w))}>
177
+ <Box width={`${width}px`} minWidth="100px" height="200px">
178
+ <Box height="100%" overflow="hidden" className={styles['demo-box']}>
179
+ <Box minWidth="200px" height="80px" className={styles['clip-target']} />
180
+ <div className={styles['size-display']}>width: {width}px</div>
181
+ </Box>
182
+ </Box>
183
+ <ResizeHandle position="end" className={styles['demo-handle']} />
184
+ </Resizable>
185
+ </VStack>
186
+ );
187
+ };
188
+
189
+ export const CustomHandle = () => {
190
+ const handleRef = useRef<HTMLDivElement>(null);
191
+ const [width, setWidth] = useState(300);
192
+
193
+ return (
194
+ <VStack gap="8px" padding="16px" minHeight="400px">
195
+ <BodyText>Custom styled handle with forwarded ref and aria attributes.</BodyText>
196
+ <Resizable onWidthResize={({ width: w }) => setWidth(Math.round(w))}>
197
+ <Box width={`${width}px`} height="200px" className={styles['demo-box']}>
198
+ <BodyText>Resizable content</BodyText>
199
+ <div className={styles['size-display']}>width: {width}px</div>
200
+ </Box>
201
+ <ResizeHandle
202
+ ref={handleRef}
203
+ position="end"
204
+ style={{
205
+ background: 'rgba(234, 67, 53, 0.4)',
206
+ width: '8px',
207
+ }}
208
+ aria-label="Resize handle"
209
+ data-testid="custom-handle"
210
+ />
211
+ </Resizable>
212
+ </VStack>
213
+ );
214
+ };