@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,47 @@
1
+ @layer tcn-system {
2
+ :where(.demo-box) {
3
+ background: var(--tcn-color-surface-secondary, #f0f0f0);
4
+ border: 1px solid var(--tcn-color-border, #ccc);
5
+ padding: 16px;
6
+ position: relative;
7
+ }
8
+
9
+ :where(.demo-handle) {
10
+ background: rgba(0, 120, 212, 0.3);
11
+ }
12
+
13
+ :where(.demo-handle:hover) {
14
+ background: rgba(0, 120, 212, 0.6);
15
+ }
16
+
17
+ :where(.size-display) {
18
+ font-family: monospace;
19
+ font-size: 12px;
20
+ color: var(--tcn-color-text-secondary, #666);
21
+ margin-top: 8px;
22
+ }
23
+
24
+ :where(.rtl-container) {
25
+ direction: rtl;
26
+ border: 2px dashed #999;
27
+ padding: 16px;
28
+ }
29
+
30
+ :where(.clip-target) {
31
+ background: #7b1fa2;
32
+ border-radius: 4px;
33
+ flex-shrink: 0;
34
+ }
35
+
36
+ :where(.nested-outer) {
37
+ background: #e8f0fe;
38
+ border: 2px solid #4285f4;
39
+ padding: 24px;
40
+ }
41
+
42
+ :where(.nested-inner) {
43
+ background: #fce8e6;
44
+ border: 2px solid #ea4335;
45
+ padding: 16px;
46
+ }
47
+ }
@@ -0,0 +1,191 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ getHandleConfig,
4
+ resolveDirection,
5
+ resolveHandleConfig,
6
+ computeResizeState,
7
+ } from '../handle_config.js';
8
+
9
+ describe('getHandleConfig', () => {
10
+ describe('edge positions', () => {
11
+ it('start → horizontal, origin start, invert true, disableDirection false', () => {
12
+ const config = getHandleConfig('start');
13
+ expect(config.horizontal).toEqual({
14
+ origin: 'start',
15
+ invert: true,
16
+ disableDirection: false,
17
+ });
18
+ expect(config.vertical).toBeUndefined();
19
+ });
20
+
21
+ it('end → horizontal, origin end, invert false, disableDirection false', () => {
22
+ const config = getHandleConfig('end');
23
+ expect(config.horizontal).toEqual({
24
+ origin: 'end',
25
+ invert: false,
26
+ disableDirection: false,
27
+ });
28
+ expect(config.vertical).toBeUndefined();
29
+ });
30
+
31
+ it('top → vertical, origin top, invert true', () => {
32
+ const config = getHandleConfig('top');
33
+ expect(config.vertical).toEqual({
34
+ origin: 'top',
35
+ invert: true,
36
+ disableDirection: true,
37
+ });
38
+ expect(config.horizontal).toBeUndefined();
39
+ });
40
+
41
+ it('bottom → vertical, origin bottom, invert false', () => {
42
+ const config = getHandleConfig('bottom');
43
+ expect(config.vertical).toEqual({
44
+ origin: 'bottom',
45
+ invert: false,
46
+ disableDirection: true,
47
+ });
48
+ expect(config.horizontal).toBeUndefined();
49
+ });
50
+ });
51
+
52
+ describe('corner positions', () => {
53
+ it('top-start → horizontal disableDirection false', () => {
54
+ const config = getHandleConfig('top-start');
55
+ expect(config.horizontal?.disableDirection).toBe(false);
56
+ expect(config.vertical?.disableDirection).toBe(true);
57
+ });
58
+
59
+ it('top-end → horizontal disableDirection false', () => {
60
+ const config = getHandleConfig('top-end');
61
+ expect(config.horizontal?.disableDirection).toBe(false);
62
+ expect(config.vertical?.disableDirection).toBe(true);
63
+ });
64
+
65
+ it('bottom-start → horizontal disableDirection false', () => {
66
+ const config = getHandleConfig('bottom-start');
67
+ expect(config.horizontal?.disableDirection).toBe(false);
68
+ expect(config.vertical?.disableDirection).toBe(true);
69
+ });
70
+
71
+ it('bottom-end → horizontal disableDirection false', () => {
72
+ const config = getHandleConfig('bottom-end');
73
+ expect(config.horizontal?.disableDirection).toBe(false);
74
+ expect(config.vertical?.disableDirection).toBe(true);
75
+ });
76
+ });
77
+ });
78
+
79
+ describe('resolveDirection', () => {
80
+ describe('LTR', () => {
81
+ it('invert=false → +1', () => {
82
+ expect(resolveDirection('ltr', false, false)).toBe(1);
83
+ });
84
+
85
+ it('invert=true → -1', () => {
86
+ expect(resolveDirection('ltr', true, false)).toBe(-1);
87
+ });
88
+
89
+ it('disableDirection has no effect in LTR', () => {
90
+ expect(resolveDirection('ltr', false, true)).toBe(1);
91
+ expect(resolveDirection('ltr', true, true)).toBe(-1);
92
+ });
93
+ });
94
+
95
+ describe('RTL', () => {
96
+ it('invert=false, disableDirection=false → flipped to -1', () => {
97
+ expect(resolveDirection('rtl', false, false)).toBe(-1);
98
+ });
99
+
100
+ it('invert=true, disableDirection=false → flipped to +1', () => {
101
+ expect(resolveDirection('rtl', true, false)).toBe(1);
102
+ });
103
+
104
+ it('disableDirection=true → no flip, same as LTR', () => {
105
+ expect(resolveDirection('rtl', false, true)).toBe(1);
106
+ expect(resolveDirection('rtl', true, true)).toBe(-1);
107
+ });
108
+ });
109
+ });
110
+
111
+ describe('computeResizeState', () => {
112
+ it('computes positive delta when moving in direction', () => {
113
+ const result = computeResizeState(200, 100, 150, 1, 200);
114
+ expect(result.newSize).toBe(250);
115
+ expect(result.totalDelta).toBe(50);
116
+ expect(result.currentDelta).toBe(50);
117
+ });
118
+
119
+ it('computes negative delta when moving against direction', () => {
120
+ const result = computeResizeState(200, 100, 50, 1, 200);
121
+ expect(result.newSize).toBe(150);
122
+ expect(result.totalDelta).toBe(-50);
123
+ expect(result.currentDelta).toBe(-50);
124
+ });
125
+
126
+ it('respects direction multiplier', () => {
127
+ const result = computeResizeState(200, 100, 150, -1, 200);
128
+ expect(result.newSize).toBe(150);
129
+ expect(result.totalDelta).toBe(-50);
130
+ expect(result.currentDelta).toBe(-50);
131
+ });
132
+
133
+ it('tracks currentDelta relative to previousSize', () => {
134
+ // First move: 200 → 250
135
+ const first = computeResizeState(200, 100, 150, 1, 200);
136
+ expect(first.newSize).toBe(250);
137
+ expect(first.currentDelta).toBe(50);
138
+
139
+ // Second move: 200 → 260 (10px further), previous was 250
140
+ const second = computeResizeState(200, 100, 160, 1, 250);
141
+ expect(second.newSize).toBe(260);
142
+ expect(second.totalDelta).toBe(60);
143
+ expect(second.currentDelta).toBe(10);
144
+ });
145
+
146
+ it('handles zero movement', () => {
147
+ const result = computeResizeState(200, 100, 100, 1, 200);
148
+ expect(result.newSize).toBe(200);
149
+ expect(result.totalDelta).toBe(0);
150
+ expect(result.currentDelta).toBe(0);
151
+ });
152
+ });
153
+
154
+ describe('resolveHandleConfig', () => {
155
+ it('resolves end edge with direction in LTR', () => {
156
+ const config = resolveHandleConfig('end', 'ltr');
157
+ expect(config.horizontal).toEqual({ origin: 'end', direction: 1 });
158
+ expect(config.vertical).toBeUndefined();
159
+ });
160
+
161
+ it('resolves start edge with direction in LTR (inverted)', () => {
162
+ const config = resolveHandleConfig('start', 'ltr');
163
+ expect(config.horizontal).toEqual({ origin: 'start', direction: -1 });
164
+ });
165
+
166
+ it('start flips direction in RTL', () => {
167
+ const ltr = resolveHandleConfig('start', 'ltr');
168
+ const rtl = resolveHandleConfig('start', 'rtl');
169
+ expect(ltr.horizontal?.direction).toBe(-1);
170
+ expect(rtl.horizontal?.direction).toBe(1);
171
+ });
172
+
173
+ it('end flips direction in RTL', () => {
174
+ const ltr = resolveHandleConfig('end', 'ltr');
175
+ const rtl = resolveHandleConfig('end', 'rtl');
176
+ expect(ltr.horizontal?.direction).toBe(1);
177
+ expect(rtl.horizontal?.direction).toBe(-1);
178
+ });
179
+
180
+ it('resolves corner with both axes', () => {
181
+ const config = resolveHandleConfig('bottom-end', 'ltr');
182
+ expect(config.horizontal).toEqual({ origin: 'end', direction: 1 });
183
+ expect(config.vertical).toEqual({ origin: 'bottom', direction: 1 });
184
+ });
185
+
186
+ it('corner horizontal flips in RTL, vertical does not', () => {
187
+ const config = resolveHandleConfig('bottom-start', 'rtl');
188
+ expect(config.horizontal?.direction).toBe(1);
189
+ expect(config.vertical?.direction).toBe(1);
190
+ });
191
+ });
@@ -0,0 +1,163 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { ResizeHandleStrategy } from '../resize_strategy.js';
3
+ import type { StartResizeParams } from '../resize_strategy.js';
4
+
5
+ const defaults: StartResizeParams = {
6
+ rectangle: {
7
+ dimensions: { width: 300, height: 200 },
8
+ position: { x: 100, y: 100 },
9
+ },
10
+ languageDirection: 'ltr',
11
+ };
12
+
13
+ function start(
14
+ strategy: ResizeHandleStrategy,
15
+ overrides: Partial<StartResizeParams> = {}
16
+ ) {
17
+ strategy.startResize({ ...defaults, ...overrides });
18
+ }
19
+
20
+ describe('ResizeHandleStrategy', () => {
21
+ describe('horizontal edge (end)', () => {
22
+ it('computes positive resize when dragging right', () => {
23
+ const strategy = new ResizeHandleStrategy('end');
24
+ start(strategy);
25
+
26
+ const result = strategy.resize({ x: 150, y: 100 });
27
+ expect(result.horizontal).toBeDefined();
28
+ expect(result.horizontal?.newSize).toBe(350);
29
+ expect(result.horizontal?.origin).toBe('end');
30
+ expect(result.horizontal?.totalDelta).toBe(50);
31
+ expect(result.horizontal?.currentDelta).toBe(50);
32
+ expect(result.vertical).toBeUndefined();
33
+ });
34
+
35
+ it('computes negative resize when dragging left', () => {
36
+ const strategy = new ResizeHandleStrategy('end');
37
+ start(strategy);
38
+
39
+ const result = strategy.resize({ x: 50, y: 100 });
40
+ expect(result.horizontal?.newSize).toBe(250);
41
+ expect(result.horizontal?.totalDelta).toBe(-50);
42
+ });
43
+ });
44
+
45
+ describe('horizontal edge (start, inverted)', () => {
46
+ it('shrinks when dragging right', () => {
47
+ const strategy = new ResizeHandleStrategy('start');
48
+ start(strategy);
49
+
50
+ const result = strategy.resize({ x: 150, y: 100 });
51
+ expect(result.horizontal?.newSize).toBe(250);
52
+ expect(result.horizontal?.origin).toBe('start');
53
+ });
54
+
55
+ it('grows when dragging left', () => {
56
+ const strategy = new ResizeHandleStrategy('start');
57
+ start(strategy);
58
+
59
+ const result = strategy.resize({ x: 50, y: 100 });
60
+ expect(result.horizontal?.newSize).toBe(350);
61
+ });
62
+ });
63
+
64
+ describe('vertical edge (bottom)', () => {
65
+ it('computes vertical resize only', () => {
66
+ const strategy = new ResizeHandleStrategy('bottom');
67
+ start(strategy);
68
+
69
+ const result = strategy.resize({ x: 100, y: 150 });
70
+ expect(result.horizontal).toBeUndefined();
71
+ expect(result.vertical).toBeDefined();
72
+ expect(result.vertical?.newSize).toBe(250);
73
+ expect(result.vertical?.origin).toBe('bottom');
74
+ });
75
+ });
76
+
77
+ describe('corner (bottom-end)', () => {
78
+ it('computes both axes simultaneously', () => {
79
+ const strategy = new ResizeHandleStrategy('bottom-end');
80
+ start(strategy);
81
+
82
+ const result = strategy.resize({ x: 160, y: 140 });
83
+ expect(result.horizontal?.newSize).toBe(360);
84
+ expect(result.horizontal?.origin).toBe('end');
85
+ expect(result.vertical?.newSize).toBe(240);
86
+ expect(result.vertical?.origin).toBe('bottom');
87
+ });
88
+ });
89
+
90
+ describe('RTL', () => {
91
+ it('start handle flips direction in RTL', () => {
92
+ const strategy = new ResizeHandleStrategy('start');
93
+ start(strategy, { languageDirection: 'rtl' });
94
+
95
+ const result = strategy.resize({ x: 150, y: 100 });
96
+ expect(result.horizontal?.newSize).toBe(350);
97
+ });
98
+
99
+ it('start handle does not flip in LTR', () => {
100
+ const strategy = new ResizeHandleStrategy('start');
101
+ start(strategy);
102
+
103
+ const result = strategy.resize({ x: 150, y: 100 });
104
+ expect(result.horizontal?.newSize).toBe(250);
105
+ });
106
+
107
+ it('end handle flips direction in RTL', () => {
108
+ const strategy = new ResizeHandleStrategy('end');
109
+ start(strategy, { languageDirection: 'rtl' });
110
+
111
+ const result = strategy.resize({ x: 150, y: 100 });
112
+ expect(result.horizontal?.newSize).toBe(250);
113
+ });
114
+ });
115
+
116
+ describe('commitResize — tracks applied dimensions', () => {
117
+ it('currentDelta tracks committed size', () => {
118
+ const strategy = new ResizeHandleStrategy('end');
119
+ start(strategy);
120
+
121
+ strategy.resize({ x: 150, y: 100 });
122
+ strategy.commitResize({ width: 350, height: 200 });
123
+
124
+ const second = strategy.resize({ x: 170, y: 100 });
125
+ expect(second.horizontal?.newSize).toBe(370);
126
+ expect(second.horizontal?.currentDelta).toBe(20);
127
+ });
128
+
129
+ it('currentDelta is correct when previous resize was clamped', () => {
130
+ const strategy = new ResizeHandleStrategy('end');
131
+ start(strategy);
132
+
133
+ strategy.resize({ x: 150, y: 100 });
134
+ strategy.commitResize({ width: 320, height: 200 });
135
+
136
+ const result = strategy.resize({ x: 160, y: 100 });
137
+ expect(result.horizontal?.newSize).toBe(360);
138
+ expect(result.horizontal?.currentDelta).toBe(40);
139
+ });
140
+ });
141
+
142
+ describe('endResize', () => {
143
+ it('returns committed sizes and origins', () => {
144
+ const strategy = new ResizeHandleStrategy('bottom-end');
145
+ start(strategy);
146
+
147
+ strategy.resize({ x: 180, y: 160 });
148
+ strategy.commitResize({ width: 380, height: 260 });
149
+
150
+ const result = strategy.endResize();
151
+ expect(result.horizontal).toEqual({ width: 380, origin: 'end' });
152
+ expect(result.vertical).toEqual({ height: 260, origin: 'bottom' });
153
+ });
154
+
155
+ it('returns start sizes if no resize occurred', () => {
156
+ const strategy = new ResizeHandleStrategy('end');
157
+ start(strategy);
158
+
159
+ const result = strategy.endResize();
160
+ expect(result.horizontal).toEqual({ width: 300, origin: 'end' });
161
+ });
162
+ });
163
+ });
@@ -0,0 +1,9 @@
1
+ import { createContext } from 'react';
2
+ import { makeContextHook } from '../../hooks/make_context_hook.js';
3
+ import type { ResizableContextValue } from './types.js';
4
+
5
+ export const ResizableContext = createContext<ResizableContextValue | undefined>(
6
+ undefined
7
+ );
8
+
9
+ export const useResizable = makeContextHook(ResizableContext, 'ResizableContext');
@@ -0,0 +1,118 @@
1
+ import type {
2
+ AxisConfig,
3
+ HandleConfig,
4
+ ResolvedHandleConfig,
5
+ ResizeHandlePosition,
6
+ } from './types.js';
7
+
8
+ /**
9
+ * Pure position → config lookup. No DOM, no React.
10
+ *
11
+ * Edges populate one axis, corners populate both.
12
+ * The React adapter reads DOM state (getBoundingClientRect, getComputedStyle)
13
+ * and feeds resolved values into resolveDirection / computeResizeState.
14
+ */
15
+
16
+ const configs: Record<ResizeHandlePosition, HandleConfig> = {
17
+ // Edges — single axis
18
+ start: {
19
+ horizontal: { origin: 'start', invert: true, disableDirection: false },
20
+ },
21
+ end: {
22
+ horizontal: { origin: 'end', invert: false, disableDirection: false },
23
+ },
24
+ top: {
25
+ vertical: { origin: 'top', invert: true, disableDirection: true },
26
+ },
27
+ bottom: {
28
+ vertical: { origin: 'bottom', invert: false, disableDirection: true },
29
+ },
30
+
31
+ // Corners — dual axis
32
+ 'top-start': {
33
+ horizontal: { origin: 'start', invert: true, disableDirection: false },
34
+ vertical: { origin: 'top', invert: true, disableDirection: true },
35
+ },
36
+ 'top-end': {
37
+ horizontal: { origin: 'end', invert: false, disableDirection: false },
38
+ vertical: { origin: 'top', invert: true, disableDirection: true },
39
+ },
40
+ 'bottom-start': {
41
+ horizontal: { origin: 'start', invert: true, disableDirection: false },
42
+ vertical: { origin: 'bottom', invert: false, disableDirection: true },
43
+ },
44
+ 'bottom-end': {
45
+ horizontal: { origin: 'end', invert: false, disableDirection: false },
46
+ vertical: { origin: 'bottom', invert: false, disableDirection: true },
47
+ },
48
+ };
49
+
50
+ export function getHandleConfig(position: ResizeHandlePosition): HandleConfig {
51
+ return configs[position];
52
+ }
53
+
54
+ /**
55
+ * Resolve the direction multiplier for a resize axis.
56
+ *
57
+ * @param languageDirection - 'ltr' or 'rtl' from getComputedStyle (passed by React adapter)
58
+ * @param invert - whether the axis inverts the delta (e.g. dragging left-handle right shrinks)
59
+ * @param disableDirection - if true, ignore RTL (physical positions like left/right)
60
+ * @returns +1 or -1
61
+ */
62
+ export function resolveDirection(
63
+ languageDirection: string,
64
+ invert: boolean,
65
+ disableDirection: boolean
66
+ ): number {
67
+ const finalInvert = languageDirection === 'rtl' && !disableDirection ? !invert : invert;
68
+ return finalInvert ? -1 : 1;
69
+ }
70
+
71
+ function resolveAxis(axis: AxisConfig, languageDirection: string) {
72
+ return {
73
+ origin: axis.origin,
74
+ direction: resolveDirection(languageDirection, axis.invert, axis.disableDirection),
75
+ };
76
+ }
77
+
78
+ /**
79
+ * Combines position lookup with direction resolution.
80
+ * The caller only needs to provide the language direction from the DOM —
81
+ * all config internals (invert, disableDirection) are resolved here.
82
+ */
83
+ export function resolveHandleConfig(
84
+ position: ResizeHandlePosition,
85
+ languageDirection: string
86
+ ): ResolvedHandleConfig {
87
+ const config = configs[position];
88
+ return {
89
+ horizontal: config.horizontal
90
+ ? resolveAxis(config.horizontal, languageDirection)
91
+ : undefined,
92
+ vertical: config.vertical
93
+ ? resolveAxis(config.vertical, languageDirection)
94
+ : undefined,
95
+ };
96
+ }
97
+
98
+ /**
99
+ * Pure resize state computation. Called per-axis on each mousemove.
100
+ *
101
+ * @param startSize - element width/height at mousedown (from getBoundingClientRect)
102
+ * @param startCoord - clientX/clientY at mousedown
103
+ * @param currentCoord - clientX/clientY at this mousemove
104
+ * @param direction - +1 or -1 from resolveDirection
105
+ * @param previousSize - the size after the last mousemove (for currentDelta)
106
+ */
107
+ export function computeResizeState(
108
+ startSize: number,
109
+ startCoord: number,
110
+ currentCoord: number,
111
+ direction: number,
112
+ previousSize: number
113
+ ): { newSize: number; totalDelta: number; currentDelta: number } {
114
+ const totalDelta = direction * (currentCoord - startCoord);
115
+ const newSize = startSize + totalDelta;
116
+ const currentDelta = newSize - previousSize;
117
+ return { newSize, totalDelta, currentDelta };
118
+ }
@@ -0,0 +1,37 @@
1
+ export { Resizable } from './resizable.js';
2
+ export type { ResizableProps } from './resizable.js';
3
+
4
+ export { ResizeHandle } from './resize_handle.js';
5
+ export type { ResizeHandleProps } from './resize_handle.js';
6
+
7
+ export { useResizable } from './context.js';
8
+
9
+ export {
10
+ getHandleConfig,
11
+ resolveDirection,
12
+ resolveHandleConfig,
13
+ computeResizeState,
14
+ } from './handle_config.js';
15
+ export { ResizeHandleStrategy } from './resize_strategy.js';
16
+ export type {
17
+ StartResizeParams,
18
+ HorizontalResizeResult,
19
+ VerticalResizeResult,
20
+ ResizeResult,
21
+ EndResizeResult,
22
+ } from './resize_strategy.js';
23
+
24
+ export type {
25
+ ResizeHandlePosition,
26
+ HandleConfig,
27
+ AxisConfig,
28
+ ResizableContextValue,
29
+ ResolvedAxisConfig,
30
+ ResolvedHandleConfig,
31
+ OnWidthResize,
32
+ OnWidthResizeEnd,
33
+ OnHeightResize,
34
+ OnHeightResizeEnd,
35
+ WidthResizeOrigin,
36
+ HeightResizeOrigin,
37
+ } from './types.js';
@@ -0,0 +1,5 @@
1
+ @layer tcn-system {
2
+ :where(.resizable-target) {
3
+ position: relative;
4
+ }
5
+ }
@@ -0,0 +1,97 @@
1
+ import React, { useMemo, useRef } from 'react';
2
+ import { clsx } from 'clsx';
3
+ import { useForkRef } from '../../hooks/use_fork_ref.js';
4
+ import { ResizableContext } from './context.js';
5
+ import type {
6
+ OnWidthResize,
7
+ OnWidthResizeEnd,
8
+ OnHeightResize,
9
+ OnHeightResizeEnd,
10
+ ResizableContextValue,
11
+ } from './types.js';
12
+ import styles from './resizable.module.css';
13
+
14
+ export interface ResizableProps {
15
+ children: React.ReactNode;
16
+ onWidthResize?: OnWidthResize;
17
+ onWidthResizeEnd?: OnWidthResizeEnd;
18
+ onHeightResize?: OnHeightResize;
19
+ onHeightResizeEnd?: OnHeightResizeEnd;
20
+ }
21
+
22
+ interface ResizableTargetProps {
23
+ className?: string;
24
+ children?: React.ReactNode;
25
+ ref?: React.Ref<HTMLElement>;
26
+ }
27
+
28
+ type ResizableTarget = React.ReactElement<ResizableTargetProps>;
29
+
30
+ function isResizableTarget(child: React.ReactNode): child is ResizableTarget {
31
+ if (!React.isValidElement(child)) return false;
32
+ const type = child.type;
33
+ if (typeof type === 'string') return true;
34
+ if (typeof type === 'function' || typeof type === 'object') {
35
+ return (type as { displayName?: string }).displayName !== 'ResizeHandle';
36
+ }
37
+ return false;
38
+ }
39
+
40
+ function isResizeHandle(child: React.ReactNode): boolean {
41
+ return React.isValidElement(child) && !isResizableTarget(child);
42
+ }
43
+
44
+ export function Resizable({
45
+ children,
46
+ onWidthResize,
47
+ onWidthResizeEnd,
48
+ onHeightResize,
49
+ onHeightResizeEnd,
50
+ }: ResizableProps) {
51
+ const targetRef = useRef<HTMLElement | null>(null);
52
+
53
+ const childArray = React.Children.toArray(children);
54
+ const targetChild = childArray.find(isResizableTarget);
55
+ const handleChildren = childArray.filter(isResizeHandle);
56
+
57
+ const contextValue = useMemo<ResizableContextValue>(
58
+ () => ({
59
+ targetRef,
60
+ onWidthResize,
61
+ onWidthResizeEnd,
62
+ onHeightResize,
63
+ onHeightResizeEnd,
64
+ }),
65
+ [onWidthResize, onWidthResizeEnd, onHeightResize, onHeightResizeEnd]
66
+ );
67
+
68
+ const forkedRef = useForkRef(targetRef, targetChild?.props.ref);
69
+
70
+ if (!targetChild) {
71
+ return (
72
+ <ResizableContext.Provider value={contextValue}>
73
+ {children}
74
+ </ResizableContext.Provider>
75
+ );
76
+ }
77
+
78
+ const clonedTarget = React.cloneElement(
79
+ targetChild,
80
+ {
81
+ ref: forkedRef,
82
+ className: clsx(
83
+ targetChild.props.className,
84
+ styles['resizable-target'],
85
+ 'tcn-resizable-target'
86
+ ),
87
+ },
88
+ targetChild.props.children,
89
+ ...handleChildren
90
+ );
91
+
92
+ return (
93
+ <ResizableContext.Provider value={contextValue}>
94
+ {clonedTarget}
95
+ </ResizableContext.Provider>
96
+ );
97
+ }