@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
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"click_away_listener.d.ts","sourceRoot":"","sources":["../../../src/utils/listeners/click_away_listener.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyC,MAAM,OAAO,CAAC;AAE9D,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,KAAK,CAAC,YAAY,CAAC;IAC7B,WAAW,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IAClE,UAAU,CAAC,EAAE,WAAW,GAAG,aAAa,GAAG,SAAS,CAAC;IACrD,UAAU,CAAC,EAAE,cAAc,GAAG,YAAY,CAAC;IAC3C,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC;IAE/C,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;CACnC;AAWD,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,UAAU,GAAG,UAAU,EAC9B,IAAI,EAAE,WAAW,GAAG,IAAI,EACxB,SAAS,EAAE,OAAO,EAClB,IAAI,EAAE,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,WAsBzB;AAED,eAAO,MAAM,iBAAiB,4FA0E7B,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import d, { useRef as p, useCallback as L, useEffect as W } from "react";
2
- import { useForkRef as T } from "./hooks/use_fork_ref.js";
2
+ import { useForkRef as T } from "../hooks/use_fork_ref.js";
3
3
  const C = {
4
4
  onClick: "click",
5
5
  onMouseDown: "mousedown",
@@ -0,0 +1 @@
1
+ {"version":3,"file":"click_away_listener.js","sources":["../../../src/utils/listeners/click_away_listener.tsx"],"sourcesContent":["import React, { useEffect, useCallback, useRef } from 'react';\nimport { useForkRef } from '../hooks/use_fork_ref.js';\nexport interface ClickAwayListenerProps {\n children: React.ReactElement;\n onClickAway: (event: React.MouseEvent | React.TouchEvent) => void;\n mouseEvent?: 'onMouseUp' | 'onMouseDown' | 'onClick';\n touchEvent?: 'onTouchStart' | 'onTouchEnd';\n isException?: (target: HTMLElement) => boolean;\n // An array of refs that wont trigger the click away listener\n refs?: React.RefObject<Element>[];\n}\nconst eventMap = {\n onClick: 'click',\n onMouseDown: 'mousedown',\n onMouseUp: 'mouseup',\n};\nconst touchMap = {\n onTouchStart: 'touchstart',\n onTouchEnd: 'touchend',\n};\n\nexport function isEventWithinElement(\n event: MouseEvent | TouchEvent,\n node: HTMLElement | null,\n activated: boolean,\n refs: (Element | null)[]\n) {\n if (!activated) {\n return true;\n }\n\n let isWithinElement: boolean;\n\n if (event.composedPath) {\n isWithinElement =\n [node, ...refs].find((ref: any) => {\n return event.composedPath().indexOf(ref) > -1;\n }) !== undefined;\n } else {\n isWithinElement =\n !document.documentElement.contains(event.target as any) ||\n [node, ...refs].find((ref: any) => {\n return ref?.contains(event.target);\n }) !== undefined;\n }\n\n return isWithinElement;\n}\n\nexport const ClickAwayListener = React.forwardRef<HTMLElement, ClickAwayListenerProps>(\n function ClickAwayListener(\n { children, mouseEvent, touchEvent, onClickAway, refs = [], isException },\n ref\n ) {\n const nodeRef = useRef<HTMLElement | null>(null);\n const startedActionWithinElementRef = useRef<boolean>(true);\n const DOMMouseEvent = eventMap[mouseEvent || 'onClick'];\n const DOMTouchEvent = touchMap[touchEvent || 'onTouchEnd'];\n const newRef = useForkRef(ref, nodeRef, (children as any).ref);\n const activatedRef = React.useRef(false);\n\n React.useEffect(() => {\n // Ensure that this component is not \"activated\" synchronously.\n // https://github.com/facebook/react/issues/20074\n window.setTimeout(() => {\n activatedRef.current = true;\n }, 0);\n return () => {\n activatedRef.current = false;\n };\n }, []);\n\n const trackAction = useCallback(\n (event: any) => {\n startedActionWithinElementRef.current = isEventWithinElement(\n event,\n nodeRef.current,\n activatedRef.current,\n refs.map(r => r.current)\n );\n },\n [refs]\n );\n\n const eventHandler = useCallback(\n (event: any) => {\n const startedFromWithinElement = startedActionWithinElementRef.current;\n const isWithinElement = isEventWithinElement(\n event,\n nodeRef.current,\n activatedRef.current,\n refs.map(r => r.current)\n );\n\n if (\n !isWithinElement &&\n !startedFromWithinElement &&\n (!isException || !isException(event.target))\n ) {\n onClickAway(event);\n }\n },\n [onClickAway, refs, isException]\n );\n\n useEffect(() => {\n document.addEventListener('mousedown', trackAction);\n document.addEventListener('touchstart', trackAction);\n document.addEventListener(DOMMouseEvent, eventHandler);\n document.addEventListener(DOMTouchEvent, eventHandler);\n return () => {\n document.removeEventListener('mousedown', trackAction);\n document.removeEventListener('touchstart', trackAction);\n document.removeEventListener(DOMMouseEvent, eventHandler);\n document.removeEventListener(DOMTouchEvent, eventHandler);\n };\n }, [DOMTouchEvent, DOMMouseEvent, eventHandler, trackAction]);\n\n return React.cloneElement(children, {\n ...children.props,\n ref: newRef,\n });\n }\n);\n"],"names":["eventMap","touchMap","isEventWithinElement","event","node","activated","refs","isWithinElement","ref","ClickAwayListener","React","children","mouseEvent","touchEvent","onClickAway","isException","nodeRef","useRef","startedActionWithinElementRef","DOMMouseEvent","DOMTouchEvent","newRef","useForkRef","activatedRef","trackAction","useCallback","r","eventHandler","startedFromWithinElement","useEffect"],"mappings":";;AAWA,MAAMA,IAAW;AAAA,EACf,SAAS;AAAA,EACT,aAAa;AAAA,EACb,WAAW;AACb,GACMC,IAAW;AAAA,EACf,cAAc;AAAA,EACd,YAAY;AACd;AAEO,SAASC,EACdC,GACAC,GACAC,GACAC,GACA;AACA,MAAI,CAACD;AACH,WAAO;AAGT,MAAIE;AAEJ,SAAIJ,EAAM,eACRI,IACE,CAACH,GAAM,GAAGE,CAAI,EAAE,KAAK,CAACE,MACbL,EAAM,aAAA,EAAe,QAAQK,CAAG,IAAI,EAC5C,MAAM,SAETD,IACE,CAAC,SAAS,gBAAgB,SAASJ,EAAM,MAAa,KACtD,CAACC,GAAM,GAAGE,CAAI,EAAE,KAAK,CAACE,MACbA,GAAK,SAASL,EAAM,MAAM,CAClC,MAAM,QAGJI;AACT;AAEO,MAAME,IAAoBC,EAAM;AAAA,EACrC,SACE,EAAE,UAAAC,GAAU,YAAAC,GAAY,YAAAC,GAAY,aAAAC,GAAa,MAAAR,IAAO,CAAA,GAAI,aAAAS,EAAA,GAC5DP,GACA;AACA,UAAMQ,IAAUC,EAA2B,IAAI,GACzCC,IAAgCD,EAAgB,EAAI,GACpDE,IAAgBnB,EAASY,KAAc,SAAS,GAChDQ,IAAgBnB,EAASY,KAAc,YAAY,GACnDQ,IAASC,EAAWd,GAAKQ,GAAUL,EAAiB,GAAG,GACvDY,IAAeb,EAAM,OAAO,EAAK;AAEvCA,IAAAA,EAAM,UAAU,OAGd,OAAO,WAAW,MAAM;AACtB,MAAAa,EAAa,UAAU;AAAA,IACzB,GAAG,CAAC,GACG,MAAM;AACX,MAAAA,EAAa,UAAU;AAAA,IACzB,IACC,CAAA,CAAE;AAEL,UAAMC,IAAcC;AAAA,MAClB,CAACtB,MAAe;AACd,QAAAe,EAA8B,UAAUhB;AAAA,UACtCC;AAAA,UACAa,EAAQ;AAAA,UACRO,EAAa;AAAA,UACbjB,EAAK,IAAI,CAAAoB,MAAKA,EAAE,OAAO;AAAA,QAAA;AAAA,MAE3B;AAAA,MACA,CAACpB,CAAI;AAAA,IAAA,GAGDqB,IAAeF;AAAA,MACnB,CAACtB,MAAe;AACd,cAAMyB,IAA2BV,EAA8B;AAQ/D,QACE,CARsBhB;AAAA,UACtBC;AAAA,UACAa,EAAQ;AAAA,UACRO,EAAa;AAAA,UACbjB,EAAK,IAAI,CAAAoB,MAAKA,EAAE,OAAO;AAAA,QAAA,KAKvB,CAACE,MACA,CAACb,KAAe,CAACA,EAAYZ,EAAM,MAAM,MAE1CW,EAAYX,CAAK;AAAA,MAErB;AAAA,MACA,CAACW,GAAaR,GAAMS,CAAW;AAAA,IAAA;AAGjC,WAAAc,EAAU,OACR,SAAS,iBAAiB,aAAaL,CAAW,GAClD,SAAS,iBAAiB,cAAcA,CAAW,GACnD,SAAS,iBAAiBL,GAAeQ,CAAY,GACrD,SAAS,iBAAiBP,GAAeO,CAAY,GAC9C,MAAM;AACX,eAAS,oBAAoB,aAAaH,CAAW,GACrD,SAAS,oBAAoB,cAAcA,CAAW,GACtD,SAAS,oBAAoBL,GAAeQ,CAAY,GACxD,SAAS,oBAAoBP,GAAeO,CAAY;AAAA,IAC1D,IACC,CAACP,GAAeD,GAAeQ,GAAcH,CAAW,CAAC,GAErDd,EAAM,aAAaC,GAAU;AAAA,MAClC,GAAGA,EAAS;AAAA,MACZ,KAAKU;AAAA,IAAA,CACN;AAAA,EACH;AACF;"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"focus_redirect.d.ts","sourceRoot":"","sources":["../../../src/utils/listeners/focus_redirect.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,eAAO,MAAM,aAAa,2FAmBxB,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"focus_redirect.js","sources":["../../../src/utils/listeners/focus_redirect.tsx"],"sourcesContent":["import React from 'react';\n\nexport interface FocusRedirectProps {\n onRedirect: () => void;\n}\n\nexport const FocusRedirect = React.forwardRef(function FocusRedirect(\n { onRedirect }: FocusRedirectProps,\n ref: React.Ref<HTMLDivElement>\n) {\n return (\n <div\n ref={ref}\n tabIndex={0}\n onFocus={onRedirect}\n style={{\n padding: 0,\n margin: 0,\n width: 0,\n height: 0,\n opacity: 0,\n position: 'relative',\n }}\n ></div>\n );\n});\n"],"names":["FocusRedirect","React","onRedirect","ref","jsx"],"mappings":";;AAMO,MAAMA,IAAgBC,EAAM,WAAW,SAC5C,EAAE,YAAAC,EAAA,GACFC,GACA;AACA,SACE,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAAD;AAAA,MACA,UAAU;AAAA,MACV,SAASD;AAAA,MACT,OAAO;AAAA,QACL,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,UAAU;AAAA,MAAA;AAAA,IACZ;AAAA,EAAA;AAGN,CAAC;"}
@@ -0,0 +1,4 @@
1
+ export * from './click_away_listener.js';
2
+ export * from './focus_redirect.js';
3
+ export * from './scroll_away_listener.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/listeners/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { ClickAwayListener as t, isEventWithinElement as o } from "./click_away_listener.js";
2
+ import { FocusRedirect as n } from "./focus_redirect.js";
3
+ import { ScrollAwayListener as l } from "./scroll_away_listener.js";
4
+ export {
5
+ t as ClickAwayListener,
6
+ n as FocusRedirect,
7
+ l as ScrollAwayListener,
8
+ o as isEventWithinElement
9
+ };
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mouse_leave_region.d.ts","sourceRoot":"","sources":["../../../src/utils/listeners/mouse_leave_region.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoB,MAAM,OAAO,CAAC;AAEzC,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;IAC7E,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,YAAY,EACZ,YAAY,EAAE,WAAW,EACzB,MAAU,GACX,EAAE,qBAAqB,QAyBvB"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mouse_leave_region.js","sources":["../../../src/utils/listeners/mouse_leave_region.tsx"],"sourcesContent":["import React, { useEffect } from 'react';\n\nexport interface MouseLeaveRegionProps {\n elementsRefs: (React.RefObject<Element> | React.MutableRefObject<Element>)[];\n onMouseLeave: () => void;\n buffer?: number;\n}\n\nexport function MouseLeaveRegion({\n onMouseLeave,\n elementsRefs: elementRefs,\n buffer = 8,\n}: MouseLeaveRegionProps) {\n useEffect(() => {\n const update = (event: MouseEvent) => {\n const intersects = elementRefs.some(ref => {\n const element = ref.current;\n if (element != null) {\n const rect = element.getBoundingClientRect();\n const overlapLeft = Math.max(event.clientX - buffer, rect.left);\n const overlapRight = Math.min(event.clientX + buffer, rect.right);\n const overlapTop = Math.max(event.clientY - buffer, rect.top);\n const overlapBottom = Math.min(event.clientY + buffer, rect.bottom);\n return overlapLeft <= overlapRight && overlapTop <= overlapBottom;\n }\n return false;\n });\n if (!intersects) {\n onMouseLeave();\n }\n };\n window.addEventListener('mousemove', update);\n return () => {\n window.removeEventListener('mousemove', update);\n };\n }, [onMouseLeave, elementRefs, buffer]);\n return null;\n}\n"],"names":["MouseLeaveRegion","onMouseLeave","elementRefs","buffer","useEffect","update","event","ref","element","rect","overlapLeft","overlapRight","overlapTop","overlapBottom"],"mappings":";AAQO,SAASA,EAAiB;AAAA,EAC/B,cAAAC;AAAA,EACA,cAAcC;AAAA,EACd,QAAAC,IAAS;AACX,GAA0B;AACxB,SAAAC,EAAU,MAAM;AACd,UAAMC,IAAS,CAACC,MAAsB;AAapC,MAZmBJ,EAAY,KAAK,CAAAK,MAAO;AACzC,cAAMC,IAAUD,EAAI;AACpB,YAAIC,KAAW,MAAM;AACnB,gBAAMC,IAAOD,EAAQ,sBAAA,GACfE,IAAc,KAAK,IAAIJ,EAAM,UAAUH,GAAQM,EAAK,IAAI,GACxDE,IAAe,KAAK,IAAIL,EAAM,UAAUH,GAAQM,EAAK,KAAK,GAC1DG,IAAa,KAAK,IAAIN,EAAM,UAAUH,GAAQM,EAAK,GAAG,GACtDI,IAAgB,KAAK,IAAIP,EAAM,UAAUH,GAAQM,EAAK,MAAM;AAClE,iBAAOC,KAAeC,KAAgBC,KAAcC;AAAA,QACtD;AACA,eAAO;AAAA,MACT,CAAC,KAECZ,EAAA;AAAA,IAEJ;AACA,kBAAO,iBAAiB,aAAaI,CAAM,GACpC,MAAM;AACX,aAAO,oBAAoB,aAAaA,CAAM;AAAA,IAChD;AAAA,EACF,GAAG,CAACJ,GAAcC,GAAaC,CAAM,CAAC,GAC/B;AACT;"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll_away_listener.d.ts","sourceRoot":"","sources":["../../../src/utils/listeners/scroll_away_listener.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAEjD,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,KAAK,CAAC,YAAY,CAAC;IAC7B,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC;IAC/C,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,6FAoC9B,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import u, { useRef as m, useEffect as a } from "react";
2
- import { useForkRef as d } from "./hooks/use_fork_ref.js";
2
+ import { useForkRef as d } from "../hooks/use_fork_ref.js";
3
3
  const L = u.forwardRef(
4
4
  function({ children: r, onScrollAway: c, isException: o }, f) {
5
5
  const n = m(null), l = d(f, n, r.ref);
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll_away_listener.js","sources":["../../../src/utils/listeners/scroll_away_listener.tsx"],"sourcesContent":["import React, { useEffect, useRef } from 'react';\nimport { useForkRef } from '../hooks/use_fork_ref.js';\nexport interface ScrollAwayListenerProps {\n children: React.ReactElement;\n isException?: (target: HTMLElement) => boolean;\n onScrollAway: () => void;\n}\n\n/**\n * This can be used for things that need to disappear when the user scrolls somewhere other\n * than the place this component surrounds.\n *\n * Mainly this will be used for tooltips and popovers. It should be used with caution and\n * correctly. If too many things are listening for other things scrolling it can become\n * quite inefficient. Within the Popover it doesn't add it to the JSX until the popover is\n * visible. That way the scroll away isn't alway listening to scroll events on the\n * document.\n */\nexport const ScrollAwayListener = React.forwardRef<HTMLElement, ScrollAwayListenerProps>(\n function ScrollAwayListener(\n { children, onScrollAway, isException }: ScrollAwayListenerProps,\n ref\n ) {\n const nodeRef = useRef<any>(null);\n const newRef = useForkRef(ref, nodeRef, (children as any).ref);\n useEffect(() => {\n const onScroll = (event: any) => {\n let insideDOM: boolean;\n if (isException && isException(event.target)) {\n insideDOM = true;\n } else if (event.composedPath) {\n insideDOM = event.composedPath().indexOf(nodeRef.current) > -1;\n } else {\n insideDOM =\n !document.documentElement.contains(event.target) ||\n nodeRef.current.contains(event.target);\n }\n\n if (!insideDOM) {\n onScrollAway();\n document.removeEventListener('scroll', onScroll, true);\n event.preventDefault();\n }\n };\n document.addEventListener('scroll', onScroll, true);\n return () => {\n document.removeEventListener('scroll', onScroll, true);\n };\n }, [onScrollAway, isException]);\n return React.cloneElement(children, {\n ...children.props,\n ref: newRef,\n });\n }\n);\n"],"names":["ScrollAwayListener","React","children","onScrollAway","isException","ref","nodeRef","useRef","newRef","useForkRef","useEffect","onScroll","event","insideDOM"],"mappings":";;AAkBO,MAAMA,IAAqBC,EAAM;AAAA,EACtC,SACE,EAAE,UAAAC,GAAU,cAAAC,GAAc,aAAAC,EAAA,GAC1BC,GACA;AACA,UAAMC,IAAUC,EAAY,IAAI,GAC1BC,IAASC,EAAWJ,GAAKC,GAAUJ,EAAiB,GAAG;AAC7D,WAAAQ,EAAU,MAAM;AACd,YAAMC,IAAW,CAACC,MAAe;AAC/B,YAAIC;AACJ,QAAIT,KAAeA,EAAYQ,EAAM,MAAM,IACzCC,IAAY,KACHD,EAAM,eACfC,IAAYD,EAAM,aAAA,EAAe,QAAQN,EAAQ,OAAO,IAAI,KAE5DO,IACE,CAAC,SAAS,gBAAgB,SAASD,EAAM,MAAM,KAC/CN,EAAQ,QAAQ,SAASM,EAAM,MAAM,GAGpCC,MACHV,EAAA,GACA,SAAS,oBAAoB,UAAUQ,GAAU,EAAI,GACrDC,EAAM,eAAA;AAAA,MAEV;AACA,sBAAS,iBAAiB,UAAUD,GAAU,EAAI,GAC3C,MAAM;AACX,iBAAS,oBAAoB,UAAUA,GAAU,EAAI;AAAA,MACvD;AAAA,IACF,GAAG,CAACR,GAAcC,CAAW,CAAC,GACvBH,EAAM,aAAaC,GAAU;AAAA,MAClC,GAAGA,EAAS;AAAA,MACZ,KAAKM;AAAA,IAAA,CACN;AAAA,EACH;AACF;"}
@@ -0,0 +1,2 @@
1
+ export * from './variations.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/system/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,2 @@
1
+
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variations.d.ts","sourceRoot":"","sources":["../../../src/utils/system/variations.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACtC,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAC9C,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,CAAC;AAC7D,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;AACrD,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,UAAU,GAAG,SAAS,GAAG,WAAW,GAAG,YAAY,CAAC"}
@@ -0,0 +1,2 @@
1
+
2
+ //# sourceMappingURL=variations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variations.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ export type HorizontalSide = 'start' | 'end';
2
+ export type VerticalSide = 'top' | 'bottom';
3
+ //# sourceMappingURL=sides.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sides.d.ts","sourceRoot":"","sources":["../../../src/utils/types/sides.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,KAAK,CAAC;AAC7C,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,QAAQ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tcn/ui",
3
- "version": "0.16.0",
3
+ "version": "0.18.0",
4
4
  "type": "module",
5
5
  "description": "",
6
6
  "author": "TCN",
@@ -41,12 +41,6 @@
41
41
  "import": "./dist/actions/index.js",
42
42
  "default": "./dist/actions/index.js"
43
43
  },
44
- "./dnd": {
45
- "@bc-monorepo/source": "./src/dnd/index.ts",
46
- "types": "./dist/dnd/index.d.ts",
47
- "import": "./dist/dnd/index.js",
48
- "default": "./dist/dnd/index.js"
49
- },
50
44
  "./feedback": {
51
45
  "@bc-monorepo/source": "./src/feedback/index.ts",
52
46
  "types": "./dist/feedback/index.d.ts",
@@ -143,8 +137,8 @@
143
137
  "libphonenumber-js": "^1.12.38",
144
138
  "react-color": "^2.19.3",
145
139
  "react-phone-number-input": "^3.4.16",
146
- "@tcn/state": "1.3.3",
147
- "@tcn/icons": "2.3.0"
140
+ "@tcn/icons": "2.4.0",
141
+ "@tcn/state": "1.3.3"
148
142
  },
149
143
  "scripts": {
150
144
  "build": "vite build",
@@ -4,7 +4,6 @@ import { Spacer } from '../../../stacks/spacer.js';
4
4
  import React from 'react';
5
5
  import { Button } from '../button/button.js';
6
6
  import { ButtonGroup } from '../button_group/button_group.js';
7
- import { SlimButton } from '../slim_button/slim_button.js';
8
7
  import { PlusIcon } from '@tcn/icons/plus_icon.js';
9
8
  import { SearchIcon } from '@tcn/icons/search_icon.js';
10
9
  import { CrossIcon } from '@tcn/icons/cross_icon.js';
@@ -110,41 +109,41 @@ export function PrimaryCustomColor() {
110
109
  );
111
110
  }
112
111
 
113
- export function SlimButtonGroup() {
112
+ export function UtilityButtonGroup() {
114
113
  return (
115
114
  <VStack gap="12px">
116
115
  <ButtonGroup hierarchy="tertiary">
117
- <SlimButton>
116
+ <Button utility>
118
117
  <CrossIcon />
119
- </SlimButton>
120
- <SlimButton>
118
+ </Button>
119
+ <Button utility>
121
120
  <SearchIcon />
122
- </SlimButton>
123
- <SlimButton>
121
+ </Button>
122
+ <Button utility>
124
123
  <PlusIcon />
125
- </SlimButton>
124
+ </Button>
126
125
  </ButtonGroup>
127
126
  <ButtonGroup hierarchy="secondary">
128
- <SlimButton>
127
+ <Button utility>
129
128
  <CrossIcon />
130
- </SlimButton>
131
- <SlimButton>
129
+ </Button>
130
+ <Button utility>
132
131
  <SearchIcon />
133
- </SlimButton>
134
- <SlimButton>
132
+ </Button>
133
+ <Button utility>
135
134
  <PlusIcon />
136
- </SlimButton>
135
+ </Button>
137
136
  </ButtonGroup>
138
137
  <ButtonGroup hierarchy="primary">
139
- <SlimButton>
138
+ <Button utility>
140
139
  <CrossIcon />
141
- </SlimButton>
142
- <SlimButton>
140
+ </Button>
141
+ <Button utility>
143
142
  <SearchIcon />
144
- </SlimButton>
145
- <SlimButton>
143
+ </Button>
144
+ <Button utility>
146
145
  <PlusIcon />
147
- </SlimButton>
146
+ </Button>
148
147
  </ButtonGroup>
149
148
  </VStack>
150
149
  );
@@ -153,12 +152,12 @@ export function SlimButtonGroup() {
153
152
  export function MixedTesting() {
154
153
  return (
155
154
  <ButtonGroup hierarchy="primary">
156
- <SlimButton>
155
+ <Button utility>
157
156
  <SearchIcon />
158
- </SlimButton>
159
- <SlimButton>
157
+ </Button>
158
+ <Button utility>
160
159
  <PlusIcon />
161
- </SlimButton>
160
+ </Button>
162
161
  <Button>
163
162
  Third
164
163
  <Spacer width="4px" />
@@ -7,6 +7,5 @@ export {
7
7
  ButtonGroup,
8
8
  type ButtonGroupProps,
9
9
  } from './button/button_group/button_group.js';
10
- export { SlimButton } from './button/slim_button/slim_button.js';
11
10
  export { Button, type ButtonProps } from './button/button/button.js';
12
11
  export { Toggle, type ToggleProps } from './toggle/toggle.js';
@@ -24,7 +24,7 @@ export default {
24
24
 
25
25
  export function Baseline() {
26
26
  return (
27
- <FieldSet minWidth="200px" padding="8px" gap="8px" maxWidth="600px">
27
+ <FieldSet minWidth="200px" maxWidth="600px">
28
28
  <Field label="Default" id="field-1">
29
29
  <Input />
30
30
  </Field>
@@ -94,7 +94,7 @@ export function ResponsiveDemo() {
94
94
  <strong>Vertical layout (VField):</strong> Screens 800px or narrower
95
95
  </BodyText>
96
96
 
97
- <FieldSet minWidth="200px" padding="8px" gap="8px" maxWidth="900px">
97
+ <FieldSet minWidth="200px" maxWidth="900px">
98
98
  <Field
99
99
  label="Email Address"
100
100
  description="Enter your primary email address"
@@ -23,7 +23,7 @@ export default {
23
23
 
24
24
  export function Baseline() {
25
25
  return (
26
- <FieldSet maxWidth="600px" minWidth="300px" padding="8px" gap="8px">
26
+ <FieldSet maxWidth="600px" minWidth="300px">
27
27
  <HField label="Default" id="field-1">
28
28
  <Input />
29
29
  </HField>
@@ -23,7 +23,7 @@ export default {
23
23
 
24
24
  export function Baseline() {
25
25
  return (
26
- <FieldSet maxWidth="350px" minWidth="150px" padding="8px" gap="8px">
26
+ <FieldSet maxWidth="350px" minWidth="150px">
27
27
  <VField label="Default" id="field-1">
28
28
  <Input />
29
29
  </VField>
@@ -1,19 +1,5 @@
1
1
  @layer tcn-system {
2
2
  :where(.field-set) {
3
3
  position: relative;
4
- border-radius: 4px;
5
- }
6
-
7
- :where(.field-set) {
8
- gap: 4px;
9
- }
10
-
11
- :where(.field-set > .field-set-body) {
12
- background: #ffffff; /* tcn neutral white */
13
- width: 100%;
14
- max-width: 100%;
15
- border-radius: 4px;
16
- gap: 8px;
17
- padding: 8px;
18
4
  }
19
5
  }
@@ -1,6 +1,10 @@
1
1
  import { Divider } from '../../layouts/divider/divider.js';
2
+ import { BodyText } from '../../typography/index.js';
3
+ import { HStack } from '../../stacks/h_stack.js';
4
+ import { VStack } from '../../stacks/v_stack.js';
2
5
  import React, { useState } from 'react';
3
6
  import { FieldSet } from './field_set.js';
7
+ import { Legend } from './legend.js';
4
8
  import {
5
9
  Input,
6
10
  MaskInput,
@@ -8,11 +12,13 @@ import {
8
12
  Multiselect,
9
13
  PhoneNumberInput,
10
14
  Select,
15
+ Switch,
11
16
  Textarea,
12
17
  UnitInput,
13
18
  Option,
14
19
  } from '../../inputs/index.js';
15
20
  import { HField } from '../field/h_field/h_field.js';
21
+ import { Field } from '../field/field.js';
16
22
 
17
23
  export default {
18
24
  title: 'Form/Field Set',
@@ -34,7 +40,8 @@ export function Baseline() {
34
40
  );
35
41
 
36
42
  return (
37
- <FieldSet maxWidth="600px" minWidth="300px" legend="Fieldset" gap="8px" padding="8px">
43
+ <FieldSet maxWidth="600px" minWidth="300px">
44
+ <Legend>Fieldset</Legend>
38
45
  <HField label="Input" id="field-1">
39
46
  <Input />
40
47
  </HField>
@@ -109,3 +116,96 @@ export function Baseline() {
109
116
  </FieldSet>
110
117
  );
111
118
  }
119
+
120
+ export function Composition() {
121
+ return (
122
+ <FieldSet maxWidth="600px" minWidth="300px">
123
+ <Legend>Personal Information</Legend>
124
+ <HField label="Name" id="comp-field-1">
125
+ <Input />
126
+ </HField>
127
+ <HField label="Email" id="comp-field-2">
128
+ <Input />
129
+ </HField>
130
+ </FieldSet>
131
+ );
132
+ }
133
+
134
+ export function Disabled() {
135
+ return (
136
+ <FieldSet maxWidth="600px" minWidth="300px" disabled>
137
+ <Legend>Locked Section</Legend>
138
+ <HField label="Name" id="disabled-field-1">
139
+ <Input />
140
+ </HField>
141
+ <HField label="Email" id="disabled-field-2">
142
+ <Input placeholder="Field in disabled FieldSet..." />
143
+ </HField>
144
+ </FieldSet>
145
+ );
146
+ }
147
+
148
+ const HOLIDAYS = [
149
+ { name: "New Year's Day", date: 'January 1, 2026', day: 'Thursday' },
150
+ { name: 'Martin Luther King Jr. Day', date: 'January 19, 2026', day: 'Monday' },
151
+ { name: "Presidents' Day", date: 'February 16, 2026', day: 'Monday' },
152
+ { name: 'Memorial Day', date: 'May 25, 2026', day: 'Monday' },
153
+ { name: 'Independence Day', date: 'July 4, 2026', day: 'Saturday' },
154
+ { name: 'Labor Day', date: 'September 7, 2026', day: 'Monday' },
155
+ { name: 'Columbus Day', date: 'October 12, 2026', day: 'Monday' },
156
+ { name: 'Veterans Day', date: 'November 11, 2026', day: 'Wednesday' },
157
+ { name: 'Thanksgiving Day', date: 'November 26, 2026', day: 'Thursday' },
158
+ { name: 'Christmas Day', date: 'December 25, 2026', day: 'Friday' },
159
+ ];
160
+
161
+ export function CustomLegend() {
162
+ const [year, setYear] = useState('2026');
163
+ const [markAll, setMarkAll] = useState(false);
164
+ const [enabled, setEnabled] = useState<Record<string, boolean>>({});
165
+
166
+ return (
167
+ <FieldSet maxWidth="400px" minWidth="300px">
168
+ <Legend>
169
+ {/* TODO: https://git.tcncloud.net/blackcat-ui/blackcat/-/issues/321 — Replace VStack + hardcoded gap with a semantic container
170
+ (e.g. Rows) that has thematic context for direction shifts in smaller layouts */}
171
+ <VStack gap="8px" hAlign="start">
172
+ <HStack width="100%" hAlign="start">
173
+ <BodyText size="lg">Suggested Federal Holidays</BodyText>
174
+ </HStack>
175
+ <BodyText emphasis="faint">
176
+ Based on your selected timezone, the following U.S. federal holidays occur in{' '}
177
+ {year}. Enable the toggle to mark a holiday as a non-working day.
178
+ </BodyText>
179
+ <Field label="Year" id="holiday-year">
180
+ <Select value={year} onChange={setYear}>
181
+ <Option value="2025" label="2025">
182
+ 2025
183
+ </Option>
184
+ <Option value="2026" label="2026">
185
+ 2026
186
+ </Option>
187
+ <Option value="2027" label="2027">
188
+ 2027
189
+ </Option>
190
+ </Select>
191
+ </Field>
192
+ <HField label="Mark all holidays as non-working days" id="holiday-mark-all">
193
+ <Switch checked={markAll} onChange={setMarkAll} />
194
+ </HField>
195
+ </VStack>
196
+ </Legend>
197
+ {HOLIDAYS.map(holiday => (
198
+ <HField
199
+ key={holiday.name}
200
+ label={`${holiday.name} - ${holiday.date} (${holiday.day})`}
201
+ id={`holiday-${holiday.name.toLowerCase().replace(/\s+/g, '-')}`}
202
+ >
203
+ <Switch
204
+ checked={markAll || !!enabled[holiday.name]}
205
+ onChange={value => setEnabled(prev => ({ ...prev, [holiday.name]: value }))}
206
+ />
207
+ </HField>
208
+ ))}
209
+ </FieldSet>
210
+ );
211
+ }
@@ -1,67 +1,53 @@
1
- import { BodyText } from '../../typography/index.js';
2
- import { HStack } from '../../stacks/h_stack.js';
3
- import { Spacer } from '../../stacks/spacer.js';
4
1
  import { VStack } from '../../stacks/v_stack.js';
5
2
  import type { VStackProps } from '../../stacks/v_stack.js';
6
3
  import { clsx } from 'clsx';
7
- import React, { useMemo } from 'react';
4
+ import React, { type FieldsetHTMLAttributes } from 'react';
5
+ import { LabelledByContext } from '../../utils/hooks/labelled_by_context.js';
6
+ import { useLabelledById } from '../../utils/hooks/labelled_by_context.js';
8
7
  import styles from './field_set.module.css';
9
8
 
10
- let fieldsetIdCount = 0;
11
-
12
- export type FieldSetProps = VStackProps<HTMLFieldSetElement> & {
13
- legend?: string;
14
- startAdornments?: React.ReactNode;
15
- endAdornments?: React.ReactNode;
9
+ export type FieldSetOwnProps = {
10
+ disabled?: boolean;
11
+ name?: string;
16
12
  };
17
13
 
18
- export const FieldSet = React.forwardRef(function FieldSet(
19
- {
20
- legend,
21
- startAdornments,
22
- endAdornments,
23
- className,
24
- children,
25
- ...props
26
- }: FieldSetProps,
27
- ref: React.Ref<HTMLFieldSetElement>
28
- ) {
29
- const id = useMemo(() => {
30
- return fieldsetIdCount++;
31
- }, []);
14
+ export type FieldSetProps = VStackProps<HTMLFieldSetElement> &
15
+ FieldSetOwnProps &
16
+ FieldsetHTMLAttributes<HTMLFieldSetElement>;
32
17
 
33
- const labelId = `field-set-${id}`;
18
+ export const FieldSet = React.forwardRef<HTMLFieldSetElement, FieldSetProps>(
19
+ function FieldSet(
20
+ {
21
+ className,
22
+ id,
23
+ children,
24
+ 'aria-labelledby': ariaLabelledBy,
25
+ ...props
26
+ }: FieldSetProps,
27
+ ref
28
+ ) {
29
+ const { labelId } = useLabelledById(id, ariaLabelledBy);
34
30
 
35
- return (
36
- <VStack
37
- as="fieldset"
38
- aria-labelledby={labelId}
39
- ref={ref}
40
- className={clsx(styles['field-set'], className, 'tcn-field-set')}
41
- {...props}
42
- >
43
- {legend && (
44
- <HStack
45
- id={labelId}
46
- className={clsx('tcn-legend', 'tcn-field-set-legend')}
47
- hAlign="start"
48
- height={props.height}
31
+ return (
32
+ <LabelledByContext.Provider value={{ labelId }}>
33
+ <VStack
34
+ as="fieldset"
35
+ aria-labelledby={labelId}
36
+ ref={ref}
37
+ id={id}
38
+ data-is-disabled={props.disabled ? 'true' : 'false'}
39
+ className={clsx(styles['field-set'], className, 'tcn-field-set')}
40
+ {...props}
49
41
  >
50
- {startAdornments}
51
- {startAdornments && <Spacer width="8px" />}
52
- <BodyText className="tcn-field-set-legend-text" size="lg">
53
- {legend}
54
- </BodyText>
55
- {endAdornments && <Spacer />}
56
- {endAdornments}
57
- </HStack>
58
- )}
59
- <VStack
60
- className={clsx('tcn-field-set-body', styles['tcn-field-set-body'])}
61
- {...props}
62
- >
63
- {children}
64
- </VStack>
65
- </VStack>
66
- );
67
- });
42
+ {/* TODO: may want to ensure Legend is rendered before rest of children.
43
+ Search over children if a match is found for legend would pop it off the children array
44
+ and manually render it in the correct slot.
45
+ Before doing this however, may want to build Slot and Slottable util to generalize the problem/solution for re-useability.
46
+ */}
47
+ {children}
48
+ <fieldset disabled></fieldset>
49
+ </VStack>
50
+ </LabelledByContext.Provider>
51
+ );
52
+ }
53
+ );
@@ -0,0 +1,44 @@
1
+ import { clsx } from 'clsx';
2
+ import { HStack, type HStackProps } from '../../stacks/h_stack.js';
3
+ import { BodyText } from '../../typography/index.js';
4
+ import { useLabelledBy } from '../../utils/hooks/labelled_by_context.js';
5
+ import styles from './field_set.module.css';
6
+
7
+ export interface LegendOwnProps extends HStackProps {
8
+ id?: string;
9
+ className?: string;
10
+ children: React.ReactNode;
11
+ }
12
+
13
+ /**
14
+ * Composable legend for FieldSet. Renders as a div (HStack) with
15
+ * aria-labelledby wiring handled automatically via context.
16
+ *
17
+ * String children are automatically wrapped in BodyText with the
18
+ * `tcn-field-set-legend-text` class for consistent theme styling.
19
+ * Non-string children (e.g. icons, custom typography) render as-is.
20
+ *
21
+ * If a custom `id` is provided, it takes precedence over the context value.
22
+ * When overriding `id`, also provide a matching `aria-labelledby` to the
23
+ * parent FieldSet so the association remains correct.
24
+ */
25
+ export const Legend = ({ id, children, className, ...props }: LegendOwnProps) => {
26
+ const ctx = useLabelledBy();
27
+ const resolvedId = id ?? ctx?.labelId;
28
+
29
+ return (
30
+ <HStack
31
+ id={resolvedId}
32
+ className={clsx(className, styles.legend, 'tcn-legend', 'tcn-field-set-legend')}
33
+ {...props}
34
+ >
35
+ {typeof children === 'string' ? (
36
+ <BodyText className="tcn-field-set-legend-text" size="lg">
37
+ {children}
38
+ </BodyText>
39
+ ) : (
40
+ children
41
+ )}
42
+ </HStack>
43
+ );
44
+ };
package/src/form/index.ts CHANGED
@@ -46,5 +46,10 @@ export {
46
46
  type OptionsFieldState,
47
47
  type OptionsFieldOptions,
48
48
  } from './field_presenters/options_field_presenter.js';
49
- export { FieldSet, type FieldSetProps } from './field_set/field_set.js';
49
+ export {
50
+ FieldSet,
51
+ type FieldSetProps,
52
+ type FieldSetOwnProps,
53
+ } from './field_set/field_set.js';
54
+ export { Legend, type LegendOwnProps } from './field_set/legend.js';
50
55
  export { FormField } from './form_field.js';