@tpzdsp/next-toolkit 2.5.1 → 3.0.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 (355) hide show
  1. package/package.json +21 -12
  2. package/src/assets/styles/globals.css +75 -95
  3. package/src/assets/styles/ol.css +57 -16
  4. package/src/assets/styles/utils.css +80 -0
  5. package/src/components/Accordion/Accordion.css +85 -0
  6. package/src/components/Accordion/Accordion.stories.tsx +264 -0
  7. package/src/components/Accordion/Accordion.tsx +98 -0
  8. package/src/components/Alert/Alert.css +129 -0
  9. package/src/components/Alert/Alert.stories.tsx +291 -0
  10. package/src/components/Alert/Alert.tsx +76 -0
  11. package/src/components/BackLink/BackLink.css +12 -0
  12. package/src/components/BackLink/BackLink.stories.tsx +52 -0
  13. package/src/components/BackLink/BackLink.tsx +44 -0
  14. package/src/components/BackToTop/BackToTop.css +23 -0
  15. package/src/components/BackToTop/BackToTop.stories.tsx +41 -0
  16. package/src/components/BackToTop/BackToTop.tsx +48 -0
  17. package/src/components/Breadcrumb/Breadcrumb.css +42 -0
  18. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +94 -0
  19. package/src/components/Breadcrumb/Breadcrumb.tsx +135 -0
  20. package/src/components/Button/Button.css +229 -0
  21. package/src/components/Button/Button.stories.tsx +140 -20
  22. package/src/components/Button/Button.tsx +53 -28
  23. package/src/components/Button/ButtonSkeleton.stories.tsx +50 -0
  24. package/src/components/Button/ButtonSkeleton.tsx +14 -0
  25. package/src/components/ButtonGroup/ButtonGroup.css +52 -0
  26. package/src/components/ButtonGroup/ButtonGroup.stories.tsx +156 -0
  27. package/src/components/ButtonGroup/ButtonGroup.tsx +50 -0
  28. package/src/components/ButtonLink/ButtonLink.css +114 -0
  29. package/src/components/ButtonLink/ButtonLink.stories.tsx +38 -51
  30. package/src/components/ButtonLink/ButtonLink.tsx +54 -23
  31. package/src/components/Card/Card.css +13 -0
  32. package/src/components/Card/Card.stories.tsx +98 -57
  33. package/src/components/Card/Card.tsx +4 -13
  34. package/src/components/CardGroup/CardGroup.css +10 -0
  35. package/src/components/CardGroup/CardGroup.stories.tsx +81 -0
  36. package/src/components/CardGroup/CardGroup.tsx +9 -0
  37. package/src/components/Checkbox/Checkbox.css +49 -0
  38. package/src/components/Checkbox/Checkbox.stories.tsx +86 -0
  39. package/src/components/Checkbox/Checkbox.tsx +32 -0
  40. package/src/components/Chip/Chip.css +54 -0
  41. package/src/components/Chip/Chip.stories.tsx +94 -0
  42. package/src/components/Chip/Chip.tsx +34 -0
  43. package/src/components/CookieBanner/CookieBanner.css +19 -0
  44. package/src/components/CookieBanner/CookieBanner.stories.tsx +52 -0
  45. package/src/components/CookieBanner/CookieBanner.tsx +55 -0
  46. package/src/components/CopyButton/CopyButton.stories.tsx +28 -0
  47. package/src/components/CopyButton/CopyButton.tsx +65 -0
  48. package/src/components/DateInput/DateInput.css +13 -0
  49. package/src/components/DateInput/DateInput.stories.tsx +131 -0
  50. package/src/components/DateInput/DateInput.test.tsx +21 -0
  51. package/src/components/DateInput/DateInput.tsx +98 -0
  52. package/src/components/Details/Details.css +28 -0
  53. package/src/components/Details/Details.stories.tsx +48 -0
  54. package/src/components/Details/Details.tsx +41 -0
  55. package/src/components/Dropdown/Dropdown.css +81 -0
  56. package/src/components/Dropdown/Dropdown.stories.tsx +131 -0
  57. package/src/components/Dropdown/Dropdown.tsx +69 -0
  58. package/src/components/ErrorBoundary/ErrorBoundary.stories.tsx +4 -39
  59. package/src/components/ErrorBoundary/ErrorBoundary.tsx +7 -4
  60. package/src/components/ErrorBoundary/ErrorFallback.tsx +32 -66
  61. package/src/components/ErrorModal/ErrorModal.css +32 -0
  62. package/src/components/ErrorModal/ErrorModal.stories.tsx +101 -0
  63. package/src/components/ErrorModal/ErrorModal.tsx +120 -0
  64. package/src/components/ErrorText/ErrorText.css +22 -0
  65. package/src/components/ErrorText/ErrorText.stories.tsx +42 -27
  66. package/src/components/ErrorText/ErrorText.tsx +10 -8
  67. package/src/components/ExternalLink/ExternalLink.css +12 -0
  68. package/src/components/ExternalLink/ExternalLink.stories.tsx +43 -0
  69. package/src/components/ExternalLink/ExternalLink.tsx +43 -0
  70. package/src/components/Fieldset/Fieldset.css +103 -0
  71. package/src/components/Fieldset/Fieldset.stories.tsx +304 -0
  72. package/src/components/Fieldset/Fieldset.tsx +38 -0
  73. package/src/components/FileUpload/FileUpload.css +85 -0
  74. package/src/components/FileUpload/FileUpload.stories.tsx +106 -0
  75. package/src/components/FileUpload/FileUpload.tsx +99 -0
  76. package/src/components/{layout/footer → Footer}/Copyright.tsx +4 -2
  77. package/src/components/Footer/Footer.stories.tsx +24 -0
  78. package/src/components/Footer/Footer.tsx +41 -0
  79. package/src/components/Footer/Licence.tsx +18 -0
  80. package/src/components/{layout/footer → Footer}/MetaLinks.tsx +8 -3
  81. package/src/components/Footer/Small/SmallFooter.tsx +43 -42
  82. package/src/components/Form/FormGroup.css +18 -0
  83. package/src/components/Form/FormGroup.stories.tsx +87 -0
  84. package/src/components/Form/FormGroup.tsx +16 -0
  85. package/src/components/{layout/header → Header}/Header.stories.tsx +26 -12
  86. package/src/components/Header/Header.test.tsx +87 -0
  87. package/src/components/Header/Header.tsx +303 -0
  88. package/src/components/Header/HeaderAppContent.tsx +120 -0
  89. package/src/components/Heading/Heading.css +40 -0
  90. package/src/components/Heading/Heading.stories.tsx +15 -5
  91. package/src/components/Heading/Heading.tsx +21 -44
  92. package/src/components/Heading/HeadingSkeleton.stories.tsx +30 -0
  93. package/src/components/Heading/HeadingSkeleton.tsx +14 -0
  94. package/src/components/Hero/Hero.css +19 -0
  95. package/src/components/Hero/Hero.stories.tsx +41 -0
  96. package/src/components/Hero/Hero.tsx +14 -0
  97. package/src/components/HintText/HintText.css +5 -0
  98. package/src/components/HintText/HintText.stories.tsx +29 -0
  99. package/src/components/HintText/HintText.tsx +13 -0
  100. package/src/components/InfoBox/InfoBox.css +38 -0
  101. package/src/components/InfoBox/InfoBox.stories.tsx +51 -403
  102. package/src/components/InfoBox/InfoBox.tsx +31 -158
  103. package/src/components/Input/Input.css +77 -0
  104. package/src/components/Input/Input.stories.tsx +123 -0
  105. package/src/components/Input/Input.tsx +74 -0
  106. package/src/components/InputGroup/InputGroup.css +140 -0
  107. package/src/components/InputGroup/InputGroup.stories.tsx +125 -0
  108. package/src/components/InputGroup/InputGroup.tsx +47 -0
  109. package/src/components/InsetText/InsetText.css +5 -0
  110. package/src/components/InsetText/InsetText.stories.tsx +30 -0
  111. package/src/components/InsetText/InsetText.tsx +9 -0
  112. package/src/components/Keyboard/Keyboard.css +9 -0
  113. package/src/components/Keyboard/Keyboard.stories.tsx +63 -0
  114. package/src/components/Keyboard/Keyboard.tsx +30 -0
  115. package/src/components/Label/Label.css +27 -0
  116. package/src/components/Label/Label.stories.tsx +40 -0
  117. package/src/components/Label/Label.tsx +17 -0
  118. package/src/components/LayerSwitcher/LayerSwitcher.css +59 -0
  119. package/src/components/LayerSwitcher/LayerSwitcher.stories.tsx +112 -0
  120. package/src/components/LayerSwitcher/LayerSwitcher.tsx +61 -0
  121. package/src/components/Layout/401/401.stories.tsx +23 -0
  122. package/src/components/Layout/401/401.tsx +1 -0
  123. package/src/components/Layout/403/403.stories.tsx +23 -0
  124. package/src/components/Layout/403/403.tsx +21 -0
  125. package/src/components/Layout/404/404.stories.tsx +23 -0
  126. package/src/components/Layout/404/404.tsx +28 -0
  127. package/src/components/Layout/500/500.stories.tsx +56 -0
  128. package/src/components/Layout/500/500.tsx +63 -0
  129. package/src/components/Layout/ButtonGroup/ButtonGroup.css +52 -0
  130. package/src/components/Layout/ButtonGroup/ButtonGroup.stories.tsx +156 -0
  131. package/src/components/Layout/ButtonGroup/ButtonGroup.tsx +50 -0
  132. package/src/components/Layout/HeadingGroup/HeadingGroup.css +21 -0
  133. package/src/components/Layout/HeadingGroup/HeadingGroup.stories.tsx +84 -0
  134. package/src/components/Layout/HeadingGroup/HeadingGroup.tsx +9 -0
  135. package/src/components/Layout/MainWrapper/MainWrapper.css +12 -0
  136. package/src/components/Layout/MainWrapper/MainWrapper.stories.tsx +36 -0
  137. package/src/components/Layout/MainWrapper/MainWrapper.tsx +14 -0
  138. package/src/components/Layout/PageShell/PageShell.css +42 -0
  139. package/src/components/Layout/PageShell/PageShell.stories.tsx +108 -0
  140. package/src/components/Layout/PageShell/PageShell.tsx +31 -0
  141. package/src/components/Layout/ParagraphGroup/ParagraphGroup.css +5 -0
  142. package/src/components/Layout/ParagraphGroup/ParagraphGroup.stories.tsx +34 -0
  143. package/src/components/Layout/ParagraphGroup/ParagraphGroup.tsx +9 -0
  144. package/src/components/Layout/Stack/Stack.css +29 -0
  145. package/src/components/Layout/Stack/Stack.stories.tsx +83 -0
  146. package/src/components/Layout/Stack/Stack.tsx +19 -0
  147. package/src/components/Layout/WidthContainer/WidthContainer.css +17 -0
  148. package/src/components/Layout/WidthContainer/WidthContainer.stories.tsx +59 -0
  149. package/src/components/Layout/WidthContainer/WidthContainer.tsx +13 -0
  150. package/src/components/Legend/Legend.css +95 -0
  151. package/src/components/Legend/Legend.stories.tsx +81 -0
  152. package/src/components/Legend/Legend.tsx +32 -0
  153. package/src/components/Legend/LegendDot.tsx +15 -0
  154. package/src/components/Legend/LegendPanel.stories.tsx +101 -0
  155. package/src/components/Legend/LegendPanel.tsx +68 -0
  156. package/src/components/Legend/LegendSkeleton.stories.tsx +87 -0
  157. package/src/components/Legend/LegendSkeleton.tsx +73 -0
  158. package/src/components/LegendSelect/LegendSelect.css +8 -0
  159. package/src/components/LegendSelect/LegendSelect.stories.tsx +80 -0
  160. package/src/components/LegendSelect/LegendSelect.tsx +26 -0
  161. package/src/components/Link/Link.css +98 -0
  162. package/src/components/Link/Link.stories.tsx +70 -0
  163. package/src/components/Link/Link.tsx +52 -0
  164. package/src/components/LinkButton/LinkButton.css +67 -0
  165. package/src/components/LinkButton/LinkButton.stories.tsx +90 -42
  166. package/src/components/LinkButton/LinkButton.tsx +50 -63
  167. package/src/components/LinkList/LinkList.css +28 -0
  168. package/src/components/LinkList/LinkList.stories.tsx +46 -0
  169. package/src/components/LinkList/LinkList.tsx +23 -0
  170. package/src/components/List/Li.tsx +9 -0
  171. package/src/components/List/List.css +9 -0
  172. package/src/components/List/OrderedList.stories.tsx +36 -0
  173. package/src/components/List/OrderedList.tsx +9 -0
  174. package/src/components/List/UnorderedList.stories.tsx +36 -0
  175. package/src/components/List/UnorderedList.tsx +9 -0
  176. package/src/components/LoadingBox/LoadingBox.css +27 -0
  177. package/src/components/LoadingBox/LoadingBox.stories.tsx +66 -0
  178. package/src/components/LoadingBox/LoadingBox.tsx +40 -0
  179. package/src/components/Modal/Modal.css +114 -0
  180. package/src/components/Modal/Modal.stories.tsx +41 -160
  181. package/src/components/Modal/Modal.tsx +132 -75
  182. package/src/components/NoScriptBanner/NoScriptBanner.css +17 -0
  183. package/src/components/NoScriptBanner/NoScriptBanner.stories.tsx +46 -0
  184. package/src/components/NoScriptBanner/NoScriptBanner.tsx +26 -0
  185. package/src/components/Paragraph/Paragraph.css +22 -0
  186. package/src/components/Paragraph/Paragraph.stories.tsx +1 -42
  187. package/src/components/Paragraph/Paragraph.tsx +4 -3
  188. package/src/components/Paragraph/ParagraphSkeleton.stories.tsx +44 -0
  189. package/src/components/Paragraph/ParagraphSkeleton.tsx +33 -0
  190. package/src/components/PasswordInput/PasswordInput.css +3 -0
  191. package/src/components/PasswordInput/PasswordInput.stories.tsx +47 -0
  192. package/src/components/PasswordInput/PasswordInput.tsx +45 -0
  193. package/src/components/PhaseBanner/PhaseBanner.css +12 -0
  194. package/src/components/PhaseBanner/PhaseBanner.stories.tsx +61 -0
  195. package/src/components/PhaseBanner/PhaseBanner.tsx +17 -0
  196. package/src/components/Popover/Popover.css +43 -0
  197. package/src/components/Popover/Popover.stories.tsx +102 -0
  198. package/src/components/Popover/Popover.tsx +40 -0
  199. package/src/components/Radio/Radio.css +69 -0
  200. package/src/components/Radio/Radio.stories.tsx +169 -0
  201. package/src/components/Radio/Radio.tsx +56 -0
  202. package/src/components/Resizable/Resizable.css +86 -0
  203. package/src/components/Resizable/Resizable.stories.tsx +123 -0
  204. package/src/components/Resizable/Resizable.tsx +86 -0
  205. package/src/components/ScrollArea/ScrollArea.css +9 -0
  206. package/src/components/ScrollArea/ScrollArea.stories.tsx +42 -0
  207. package/src/components/ScrollArea/ScrollArea.tsx +11 -0
  208. package/src/components/SearchBox/SearchBox.stories.tsx +88 -0
  209. package/src/components/SearchBox/SearchBox.tsx +36 -0
  210. package/src/components/Select/AsyncSelect.stories.tsx +84 -0
  211. package/src/components/Select/AsyncSelect.tsx +41 -0
  212. package/src/components/Select/Select.css +194 -0
  213. package/src/components/Select/Select.stories.tsx +162 -0
  214. package/src/components/Select/Select.test.tsx +36 -0
  215. package/src/components/Select/Select.tsx +291 -0
  216. package/src/components/Select/SelectSkeleton.stories.tsx +51 -0
  217. package/src/components/Select/SelectSkeleton.tsx +23 -0
  218. package/src/components/Separator/Separator.css +41 -0
  219. package/src/components/Separator/Separator.stories.tsx +63 -0
  220. package/src/components/Separator/Separator.tsx +42 -0
  221. package/src/components/Sidebar/ResizableSidebar.tsx +118 -0
  222. package/src/components/Sidebar/Sidebar.css +203 -0
  223. package/src/components/Sidebar/Sidebar.stories.tsx +438 -0
  224. package/src/components/Sidebar/Sidebar.tsx +225 -0
  225. package/src/components/Skeleton/Skeleton.css +16 -0
  226. package/src/components/Skeleton/Skeleton.stories.tsx +39 -0
  227. package/src/components/Skeleton/Skeleton.tsx +12 -0
  228. package/src/components/SkipLink/SkipLink.css +16 -0
  229. package/src/components/SkipLink/SkipLink.stories.tsx +40 -0
  230. package/src/components/SkipLink/SkipLink.tsx +15 -0
  231. package/src/components/Spinner/Spinner.css +30 -0
  232. package/src/components/Spinner/Spinner.stories.tsx +36 -0
  233. package/src/components/Spinner/Spinner.tsx +45 -0
  234. package/src/components/StartButton/StartButton.css +13 -0
  235. package/src/components/StartButton/StartButton.stories.tsx +68 -0
  236. package/src/components/StartButton/StartButton.tsx +50 -0
  237. package/src/components/SubmitButton/SubmitButton.stories.tsx +31 -0
  238. package/src/components/SubmitButton/SubmitButton.tsx +14 -0
  239. package/src/components/SummaryList/SummaryList.css +71 -0
  240. package/src/components/SummaryList/SummaryList.stories.tsx +211 -0
  241. package/src/components/SummaryList/SummaryList.tsx +41 -0
  242. package/src/components/SummaryList/SummaryListSkeleton.stories.tsx +67 -0
  243. package/src/components/SummaryList/SummaryListSkeleton.tsx +40 -0
  244. package/src/components/Tabs/Tabs.css +84 -0
  245. package/src/components/Tabs/Tabs.stories.tsx +133 -0
  246. package/src/components/Tabs/Tabs.tsx +66 -0
  247. package/src/components/Tag/Tag.css +8 -0
  248. package/src/components/Tag/Tag.stories.tsx +96 -0
  249. package/src/components/Tag/Tag.tsx +34 -0
  250. package/src/components/Textarea/Textarea.css +38 -0
  251. package/src/components/Textarea/Textarea.stories.tsx +131 -0
  252. package/src/components/Textarea/Textarea.tsx +50 -0
  253. package/src/components/TextareaCount/TextareaCount.css +17 -0
  254. package/src/components/TextareaCount/TextareaCount.stories.tsx +131 -0
  255. package/src/components/TextareaCount/TextareaCount.tsx +108 -0
  256. package/src/components/Tooltip/Tooltip.css +43 -0
  257. package/src/components/Tooltip/Tooltip.stories.tsx +110 -0
  258. package/src/components/Tooltip/Tooltip.tsx +53 -0
  259. package/src/components/WarningText/WarningText.css +20 -0
  260. package/src/components/WarningText/WarningText.stories.tsx +29 -0
  261. package/src/components/WarningText/WarningText.tsx +17 -0
  262. package/src/components/index.ts +342 -61
  263. package/src/errors/errorDisplay.ts +70 -0
  264. package/src/errors/index.ts +1 -0
  265. package/src/http/constants.ts +1 -0
  266. package/src/http/fetch.ts +7 -3
  267. package/src/map/LayerSwitcherPanel.tsx +2 -173
  268. package/src/map/LegendShim.tsx +2 -0
  269. package/src/map/MapComponent.tsx +7 -17
  270. package/src/map/MapControlsOverlay.tsx +68 -175
  271. package/src/map/Popup.tsx +71 -24
  272. package/src/map/geocoder/Geocoder.test.tsx +46 -3
  273. package/src/map/geocoder/Geocoder.tsx +155 -278
  274. package/src/map/index.ts +1 -1
  275. package/src/types/utils.ts +0 -10
  276. package/src/utils/components.ts +96 -0
  277. package/src/utils/index.ts +1 -0
  278. package/src/utils/utils.ts +0 -12
  279. package/src/assets/images/defra-logo.svg +0 -51
  280. package/src/assets/images/ea-logo.svg +0 -58
  281. package/src/assets/images/ogl.svg +0 -1
  282. package/src/components/Button/Button.test.tsx +0 -53
  283. package/src/components/ButtonLink/ButtonLink.test.tsx +0 -154
  284. package/src/components/Card/Card.test.tsx +0 -51
  285. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +0 -75
  286. package/src/components/ErrorBoundary/ErrorFallback.test.tsx +0 -107
  287. package/src/components/ErrorText/ErrorText.test.tsx +0 -33
  288. package/src/components/Heading/Heading.test.tsx +0 -22
  289. package/src/components/Hint/Hint.stories.tsx +0 -58
  290. package/src/components/Hint/Hint.test.tsx +0 -33
  291. package/src/components/Hint/Hint.tsx +0 -12
  292. package/src/components/InfoBox/InfoBox.test.tsx +0 -330
  293. package/src/components/InfoBox/types.ts +0 -6
  294. package/src/components/LinkButton/LinkButton.test.tsx +0 -177
  295. package/src/components/Modal/Modal.test.tsx +0 -246
  296. package/src/components/NotificationBanner/NotificationBanner.stories.tsx +0 -45
  297. package/src/components/NotificationBanner/NotificationBanner.test.tsx +0 -58
  298. package/src/components/NotificationBanner/NotificationBanner.tsx +0 -45
  299. package/src/components/Paragraph/Paragraph.test.tsx +0 -10
  300. package/src/components/SlidingPanel/SlidingPanel.stories.tsx +0 -129
  301. package/src/components/SlidingPanel/SlidingPanel.test.tsx +0 -83
  302. package/src/components/SlidingPanel/SlidingPanel.tsx +0 -258
  303. package/src/components/accordion/Accordion.stories.tsx +0 -234
  304. package/src/components/accordion/Accordion.test.tsx +0 -192
  305. package/src/components/accordion/Accordion.tsx +0 -73
  306. package/src/components/backToTop/BackToTop.stories.tsx +0 -407
  307. package/src/components/backToTop/BackToTop.test.tsx +0 -57
  308. package/src/components/backToTop/BackToTop.tsx +0 -134
  309. package/src/components/chip/Chip.stories.tsx +0 -61
  310. package/src/components/chip/Chip.test.tsx +0 -31
  311. package/src/components/chip/Chip.tsx +0 -23
  312. package/src/components/container/Container.tsx +0 -42
  313. package/src/components/cookieBanner/CookieBanner.stories.tsx +0 -257
  314. package/src/components/cookieBanner/CookieBanner.test.tsx +0 -68
  315. package/src/components/cookieBanner/CookieBanner.tsx +0 -73
  316. package/src/components/divider/RuleDivider.stories.tsx +0 -254
  317. package/src/components/divider/RuleDivider.test.tsx +0 -164
  318. package/src/components/divider/RuleDivider.tsx +0 -23
  319. package/src/components/dropdown/DropdownMenu.test.tsx +0 -211
  320. package/src/components/dropdown/DropdownMenu.tsx +0 -108
  321. package/src/components/dropdown/useDropdownMenu.ts +0 -249
  322. package/src/components/form/Input.stories.tsx +0 -435
  323. package/src/components/form/Input.test.tsx +0 -206
  324. package/src/components/form/Input.tsx +0 -23
  325. package/src/components/form/TextArea.stories.tsx +0 -464
  326. package/src/components/form/TextArea.test.tsx +0 -232
  327. package/src/components/form/TextArea.tsx +0 -23
  328. package/src/components/layout/footer/Footer.tsx +0 -26
  329. package/src/components/layout/footer/Licence.tsx +0 -19
  330. package/src/components/layout/header/Header.test.tsx +0 -36
  331. package/src/components/layout/header/Header.tsx +0 -90
  332. package/src/components/layout/header/HeaderAuthClient.test.tsx +0 -45
  333. package/src/components/layout/header/HeaderAuthClient.tsx +0 -40
  334. package/src/components/layout/header/HeaderNavClient.test.tsx +0 -44
  335. package/src/components/layout/header/HeaderNavClient.tsx +0 -67
  336. package/src/components/link/ExternalLink.test.tsx +0 -102
  337. package/src/components/link/ExternalLink.tsx +0 -29
  338. package/src/components/link/Link.tsx +0 -26
  339. package/src/components/select/Select.stories.tsx +0 -336
  340. package/src/components/select/Select.test.tsx +0 -474
  341. package/src/components/select/Select.tsx +0 -125
  342. package/src/components/select/SelectSkeleton.stories.tsx +0 -194
  343. package/src/components/select/SelectSkeleton.test.tsx +0 -104
  344. package/src/components/select/SelectSkeleton.tsx +0 -23
  345. package/src/components/select/common.ts +0 -3
  346. package/src/components/select/index.ts +0 -18
  347. package/src/components/skipLink/SkipLink.stories.tsx +0 -346
  348. package/src/components/skipLink/SkipLink.test.tsx +0 -22
  349. package/src/components/skipLink/SkipLink.tsx +0 -49
  350. package/src/map/LegendPanel.tsx +0 -85
  351. /package/src/{components → assets}/images/DefraLogo.tsx +0 -0
  352. /package/src/{components → assets}/images/EaLogo.tsx +0 -0
  353. /package/src/{components → assets}/images/OglLogo.tsx +0 -0
  354. /package/src/components/{googleAnalytics → GoogleAnalytics}/GlobalVars.tsx +0 -0
  355. /package/src/components/{googleAnalytics → GoogleAnalytics}/GoogleAnalytics.tsx +0 -0
@@ -0,0 +1,291 @@
1
+ 'use client';
2
+
3
+ import { useId } from 'react';
4
+
5
+ import { LuChevronDown, LuLoaderCircle, LuX } from 'react-icons/lu';
6
+ import type {
7
+ ClassNamesConfig,
8
+ ClearIndicatorProps,
9
+ ContainerProps,
10
+ DropdownIndicatorProps,
11
+ GroupBase,
12
+ GroupHeadingProps,
13
+ InputProps,
14
+ LoadingIndicatorProps,
15
+ MultiValueProps,
16
+ OnChangeValue,
17
+ Props as ReactSelectProps,
18
+ } from 'react-select';
19
+ import { components, default as ReactSelect } from 'react-select';
20
+
21
+ import { cn } from '../../utils/components';
22
+ import { Separator } from '../Separator/Separator';
23
+
24
+ export type SelectTagsPlacement = 'inline' | 'below';
25
+
26
+ export type SelectProps<
27
+ Option = unknown,
28
+ IsMulti extends boolean = false,
29
+ Group extends GroupBase<Option> = GroupBase<Option>,
30
+ > = Omit<ReactSelectProps<Option, IsMulti, Group>, 'className'> & {
31
+ invalid?: boolean;
32
+ /** Where selected values render for a multi-select. `below` keeps the control a fixed
33
+ * height and lists selected values underneath instead of as inline chips. */
34
+ tagsPlacement?: SelectTagsPlacement;
35
+ /** Suppresses the built-in loading spinner in the indicators area, for callers that
36
+ * render their own loading state elsewhere (e.g. a fixed-width InputSuffix) instead
37
+ * of having the indicators container grow/shrink as it mounts and unmounts. */
38
+ hideLoadingIndicator?: boolean;
39
+ /** react-select's own Props type doesn't declare aria-describedby at all (only
40
+ * aria-errormessage/invalid/label/labelledby/live), and renderInput() computes its own
41
+ * hardcoded value for the real input (pointing at its internal live-region/placeholder),
42
+ * overwriting anything already there. Declaring it explicitly here is what makes this
43
+ * actually work: the Input override below reads it back out of selectProps (react-select's
44
+ * untouched copy of everything passed to Select) and merges it alongside react-select's
45
+ * own separately-computed value, rather than it being silently dropped. */
46
+ 'aria-describedby'?: string;
47
+ };
48
+
49
+ const buildClassNames = <Option, IsMulti extends boolean, Group extends GroupBase<Option>>(
50
+ invalid: boolean,
51
+ userClassNames?: ClassNamesConfig<Option, IsMulti, Group>,
52
+ ): ClassNamesConfig<Option, IsMulti, Group> => ({
53
+ container: (props) => cn('cn-select-container', userClassNames?.container?.(props)),
54
+ control: (props) =>
55
+ cn(
56
+ 'cn-select-control',
57
+ props.isFocused && 'cn-select-control-focused',
58
+ invalid && !props.isFocused && 'cn-select-control-invalid',
59
+ props.isDisabled && 'cn-select-control-disabled',
60
+ userClassNames?.control?.(props),
61
+ ),
62
+ valueContainer: (props) =>
63
+ cn(
64
+ 'cn-select-value-container',
65
+ props.isMulti && 'cn-select-value-container-multi',
66
+ userClassNames?.valueContainer?.(props),
67
+ ),
68
+ singleValue: (props) => cn('cn-select-single-value', userClassNames?.singleValue?.(props)),
69
+ placeholder: (props) => cn('cn-select-placeholder', userClassNames?.placeholder?.(props)),
70
+ input: (props) => cn('cn-select-input', userClassNames?.input?.(props)),
71
+ indicatorsContainer: (props) =>
72
+ cn('cn-select-indicators', userClassNames?.indicatorsContainer?.(props)),
73
+ indicatorSeparator: () => 'hidden',
74
+ dropdownIndicator: (props) =>
75
+ cn(
76
+ 'cn-select-dropdown-indicator',
77
+ props.selectProps.menuIsOpen && 'cn-select-dropdown-indicator-open',
78
+ userClassNames?.dropdownIndicator?.(props),
79
+ ),
80
+ clearIndicator: (props) =>
81
+ cn('cn-select-clear-indicator', userClassNames?.clearIndicator?.(props)),
82
+ loadingIndicator: (props) =>
83
+ cn('cn-select-loading-indicator', userClassNames?.loadingIndicator?.(props)),
84
+ menu: (props) => cn('cn-select-menu', userClassNames?.menu?.(props)),
85
+ menuList: (props) => cn('cn-select-menu-list', userClassNames?.menuList?.(props)),
86
+ option: (props) =>
87
+ cn(
88
+ 'cn-select-option',
89
+ props.isFocused && !props.isSelected && 'cn-select-option-focused',
90
+ props.isSelected && 'cn-select-option-selected',
91
+ props.isDisabled && 'cn-select-option-disabled',
92
+ userClassNames?.option?.(props),
93
+ ),
94
+ noOptionsMessage: (props) =>
95
+ cn('cn-select-no-options', userClassNames?.noOptionsMessage?.(props)),
96
+ multiValue: (props) => cn('cn-select-multi-value', userClassNames?.multiValue?.(props)),
97
+ multiValueLabel: (props) =>
98
+ cn('cn-select-multi-value-label', userClassNames?.multiValueLabel?.(props)),
99
+ multiValueRemove: (props) =>
100
+ cn('cn-select-multi-value-remove', userClassNames?.multiValueRemove?.(props)),
101
+ group: (props) => cn('cn-select-group', userClassNames?.group?.(props)),
102
+ groupHeading: (props) => cn('cn-select-group-heading', userClassNames?.groupHeading?.(props)),
103
+ });
104
+
105
+ const DropdownIndicator = <Option, IsMulti extends boolean, Group extends GroupBase<Option>>(
106
+ props: DropdownIndicatorProps<Option, IsMulti, Group>,
107
+ ) => (
108
+ <LuChevronDown
109
+ className={props.getClassNames('dropdownIndicator', props)}
110
+ strokeWidth={2.5}
111
+ aria-hidden
112
+ />
113
+ );
114
+
115
+ const ClearIndicator = <Option, IsMulti extends boolean, Group extends GroupBase<Option>>(
116
+ props: ClearIndicatorProps<Option, IsMulti, Group>,
117
+ ) => (
118
+ <components.ClearIndicator {...props}>
119
+ <LuX className="w-full h-full" strokeWidth={2.5} aria-hidden />
120
+ </components.ClearIndicator>
121
+ );
122
+
123
+ const LoadingIndicator = <Option, IsMulti extends boolean, Group extends GroupBase<Option>>(
124
+ props: LoadingIndicatorProps<Option, IsMulti, Group>,
125
+ ) => (
126
+ <LuLoaderCircle
127
+ className={cn(props.getClassNames('loadingIndicator', props), 'animate-spin')}
128
+ aria-hidden
129
+ />
130
+ );
131
+
132
+ // react-select's own renderInput() computes its own aria-describedby (pointing
133
+ // at its internal live-region or placeholder element) and spreads it onto the
134
+ // real <input> last, after any of its own props, so it always wins over
135
+ // whatever Select was originally given, see SelectProps['aria-describedby'].
136
+ // selectProps is react-select's own full, unfiltered instance props object
137
+ // though, still holding that original untouched value, so it survives there
138
+ // and can be merged back in, just needs a cast, react-select's own Props type
139
+ // doesn't declare aria-describedby at all (unlike its aria-invalid/label/etc,
140
+ // which it does forward as-is, no merging needed for those).
141
+ const Input = <Option, IsMulti extends boolean, Group extends GroupBase<Option>>(
142
+ props: InputProps<Option, IsMulti, Group>,
143
+ ) => {
144
+ const external = (props.selectProps as { 'aria-describedby'?: string })['aria-describedby'];
145
+ const ariaDescribedBy = [props['aria-describedby'], external].filter(Boolean).join(' ');
146
+
147
+ return <components.Input {...props} aria-describedby={ariaDescribedBy || undefined} />;
148
+ };
149
+
150
+ const GroupHeading = <Option, IsMulti extends boolean, Group extends GroupBase<Option>>(
151
+ props: GroupHeadingProps<Option, IsMulti, Group>,
152
+ ) => <Separator className={props.getClassNames('groupHeading', props)}>{props.children}</Separator>;
153
+
154
+ const MultiValue = <Option, IsMulti extends boolean, Group extends GroupBase<Option>>({
155
+ removeProps,
156
+ isDisabled,
157
+ selectProps,
158
+ data,
159
+ }: MultiValueProps<Option, IsMulti, Group>) => {
160
+ const label = selectProps.getOptionLabel(data);
161
+
162
+ return (
163
+ <button
164
+ type="button"
165
+ className="cn-select-multi-value"
166
+ aria-label={`Remove ${label}`}
167
+ disabled={isDisabled}
168
+ onClick={removeProps.onClick as unknown as React.MouseEventHandler<HTMLButtonElement>}
169
+ onMouseDown={removeProps.onMouseDown as unknown as React.MouseEventHandler<HTMLButtonElement>}
170
+ onTouchEnd={removeProps.onTouchEnd as unknown as React.TouchEventHandler<HTMLButtonElement>}
171
+ >
172
+ {label}
173
+ <LuX aria-hidden className="cn-select-multi-value-remove" strokeWidth={2.5} />
174
+ </button>
175
+ );
176
+ };
177
+
178
+ const HiddenMultiValue = () => null;
179
+
180
+ const HiddenLoadingIndicator = () => null;
181
+
182
+ const SelectContainerWithTagsBelow = <
183
+ Option,
184
+ IsMulti extends boolean,
185
+ Group extends GroupBase<Option>,
186
+ >(
187
+ props: ContainerProps<Option, IsMulti, Group>,
188
+ ) => {
189
+ const { children, selectProps, getValue, hasValue } = props;
190
+ const { getOptionLabel, getOptionValue, onChange, name, isDisabled } = selectProps;
191
+
192
+ const handleRemove = (option: Option) => {
193
+ const candidate = getOptionValue(option);
194
+ const newValue = getValue().filter((item) => getOptionValue(item) !== candidate);
195
+
196
+ onChange(newValue as OnChangeValue<Option, IsMulti>, {
197
+ action: 'remove-value',
198
+ removedValue: option,
199
+ name,
200
+ });
201
+ };
202
+
203
+ return (
204
+ <components.SelectContainer {...props}>
205
+ {children}
206
+
207
+ {hasValue ? (
208
+ <div className="cn-select-tags">
209
+ {getValue().map((option) => (
210
+ <button
211
+ key={getOptionValue(option)}
212
+ type="button"
213
+ className="cn-select-tag"
214
+ aria-label={`Remove ${getOptionLabel(option)}`}
215
+ disabled={isDisabled}
216
+ onClick={() => handleRemove(option)}
217
+ >
218
+ {getOptionLabel(option)}
219
+ <LuX aria-hidden className="cn-select-multi-value-remove" strokeWidth={2.5} />
220
+ </button>
221
+ ))}
222
+ </div>
223
+ ) : null}
224
+ </components.SelectContainer>
225
+ );
226
+ };
227
+
228
+ export const Select = <
229
+ Option = unknown,
230
+ IsMulti extends boolean = false,
231
+ Group extends GroupBase<Option> = GroupBase<Option>,
232
+ >({
233
+ classNames: userClassNames,
234
+ styles: userStyles,
235
+ invalid = false,
236
+ isMulti,
237
+ tagsPlacement = 'inline',
238
+ hideLoadingIndicator = false,
239
+ ...props
240
+ }: SelectProps<Option, IsMulti, Group>) => {
241
+ const instanceId = useId();
242
+ const showTagsBelow = isMulti && tagsPlacement === 'below';
243
+
244
+ return (
245
+ <ReactSelect
246
+ menuPortalTarget={typeof document !== 'undefined' ? document.body : null}
247
+ menuPosition="fixed"
248
+ tabSelectsValue={false}
249
+ {...props}
250
+ isMulti={isMulti}
251
+ instanceId={instanceId}
252
+ unstyled
253
+ aria-invalid={invalid ? 'true' : undefined}
254
+ styles={{
255
+ ...userStyles,
256
+ // react-select injects outline: 0 !important and transition via Emotion
257
+ // on the control div even in unstyled mode. By returning an empty object
258
+ // (not spreading base) we prevent that class from being generated, so
259
+ // our CSS classes can set outline freely without needing !important.
260
+ control: (_, controlProps) => ({
261
+ ...userStyles?.control?.({}, controlProps),
262
+ }),
263
+ // Removes the `pointer-events: none` style.
264
+ container: (_, containerProps) => ({
265
+ ...userStyles?.container?.({}, containerProps),
266
+ }),
267
+ // react-select renders the portal wrapper itself with an inline
268
+ // style, a separate element from .cn-select-menu (which already
269
+ // has cn-z-overlay), so it isn't reachable via our own CSS classes
270
+ // and has to independently match that same value (999999) here, or
271
+ // this outer wrapper's z-index is what actually determines stacking
272
+ // against unrelated high z-index content elsewhere on the page.
273
+ menuPortal: (base, portalProps) => ({
274
+ ...base,
275
+ zIndex: 999999,
276
+ ...userStyles?.menuPortal?.(base, portalProps),
277
+ }),
278
+ }}
279
+ classNames={buildClassNames<Option, IsMulti, Group>(invalid, userClassNames)}
280
+ components={{
281
+ Input,
282
+ DropdownIndicator,
283
+ ClearIndicator,
284
+ LoadingIndicator: hideLoadingIndicator ? HiddenLoadingIndicator : LoadingIndicator,
285
+ MultiValue: showTagsBelow ? HiddenMultiValue : MultiValue,
286
+ GroupHeading,
287
+ ...(showTagsBelow ? { SelectContainer: SelectContainerWithTagsBelow } : {}),
288
+ }}
289
+ />
290
+ );
291
+ };
@@ -0,0 +1,51 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+
3
+ import { Select } from './Select';
4
+ import { SelectSkeleton } from './SelectSkeleton';
5
+
6
+ const meta: Meta<typeof SelectSkeleton> = {
7
+ title: 'Components/Select/Skeleton',
8
+ component: SelectSkeleton,
9
+ tags: ['autodocs'],
10
+ parameters: {
11
+ docs: {
12
+ description: {
13
+ component:
14
+ 'Placeholder for a Select while its options are loading. Reuses cn-select-container/control/value-container/indicators directly, so its dimensions always match the real control exactly, no layout shift when it swaps in.',
15
+ },
16
+ },
17
+ },
18
+ };
19
+
20
+ export default meta;
21
+
22
+ type Story = StoryObj<typeof SelectSkeleton>;
23
+
24
+ export const Default: Story = {
25
+ render: () => (
26
+ <div className="w-64">
27
+ <SelectSkeleton />
28
+ </div>
29
+ ),
30
+ };
31
+
32
+ export const SideBySide: Story = {
33
+ parameters: {
34
+ docs: {
35
+ description: {
36
+ story: 'The skeleton and the real control line up exactly, same height, border, padding.',
37
+ },
38
+ },
39
+ },
40
+ render: () => (
41
+ <div className="flex gap-gds-4">
42
+ <div className="w-64">
43
+ <SelectSkeleton />
44
+ </div>
45
+
46
+ <div className="w-64">
47
+ <Select options={[{ value: 'a', label: 'Option A' }]} placeholder="Select an option" />
48
+ </div>
49
+ </div>
50
+ ),
51
+ };
@@ -0,0 +1,23 @@
1
+ import type { ComponentProps } from 'react';
2
+
3
+ import { cn } from '../../utils/components';
4
+ import { Skeleton } from '../Skeleton/Skeleton';
5
+
6
+ export type SelectSkeletonProps = ComponentProps<'div'> & {
7
+ /** Explicitly disallow children, a skeleton is a placeholder shape, not a content wrapper. */
8
+ children?: never;
9
+ };
10
+
11
+ export const SelectSkeleton = ({ className, ...props }: SelectSkeletonProps) => (
12
+ <div aria-hidden className={cn('cn-select-container', className)} {...props}>
13
+ <div className="cn-select-control">
14
+ <div className="cn-select-value-container">
15
+ <Skeleton className="cn-select-skeleton-value" />
16
+ </div>
17
+
18
+ <div className="cn-select-indicators">
19
+ <Skeleton className="cn-select-skeleton-indicator" />
20
+ </div>
21
+ </div>
22
+ </div>
23
+ );
@@ -0,0 +1,41 @@
1
+ @layer components {
2
+ /*
3
+ MARK: Separator
4
+ GDS section break using the divider colour.
5
+ No external margin. Spacing belongs to the surrounding layout.
6
+
7
+ Vertical separators require a flex parent to get natural height via
8
+ align-self: stretch. Without one, set an explicit height on the separator.
9
+ */
10
+ .cn-separator {
11
+ @apply relative flex items-center justify-center;
12
+ }
13
+
14
+ .cn-separator[data-orientation='horizontal'] {
15
+ @apply w-full max-h-px;
16
+ }
17
+
18
+ .cn-separator[data-orientation='vertical'] {
19
+ @apply self-stretch flex-col max-w-px;
20
+ }
21
+
22
+ /* MARK: Line */
23
+
24
+ .cn-separator-line {
25
+ @apply absolute shrink-0 bg-divider border-none;
26
+ }
27
+
28
+ .cn-separator-line[data-orientation='horizontal'] {
29
+ @apply w-full top-1/2 -translate-y-1/2 h-px;
30
+ }
31
+
32
+ .cn-separator-line[data-orientation='vertical'] {
33
+ @apply h-full left-1/2 -translate-x-1/2 w-px;
34
+ }
35
+
36
+ /* MARK: Content */
37
+
38
+ .cn-separator-content {
39
+ @apply relative z-10;
40
+ }
41
+ }
@@ -0,0 +1,63 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+
3
+ import { Separator } from './Separator';
4
+
5
+ const meta: Meta<typeof Separator> = {
6
+ title: 'Components/Separator',
7
+ component: Separator,
8
+ tags: ['autodocs'],
9
+ parameters: {
10
+ docs: {
11
+ description: {
12
+ component: 'Use Separator to visually divide content sections in layouts and forms.',
13
+ },
14
+ },
15
+ },
16
+ argTypes: {
17
+ orientation: {
18
+ control: { type: 'radio' },
19
+ options: ['horizontal', 'vertical'],
20
+ },
21
+ decorative: { control: 'boolean' },
22
+ },
23
+ args: {
24
+ orientation: 'horizontal',
25
+ decorative: true,
26
+ },
27
+ };
28
+
29
+ export default meta;
30
+
31
+ type Story = StoryObj<typeof Separator>;
32
+
33
+ export const Horizontal: Story = {};
34
+
35
+ export const Vertical: Story = {
36
+ args: {
37
+ orientation: 'vertical',
38
+ decorative: false,
39
+ // hardcode height for example otherwise it will collapse to 0px
40
+ style: { height: '50px' },
41
+ },
42
+ };
43
+
44
+ export const WithText: Story = {
45
+ args: {
46
+ orientation: 'horizontal',
47
+ children: 'Section Title',
48
+ },
49
+ };
50
+
51
+ export const InlineExample: Story = {
52
+ // Vertical separators can't have children.
53
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
54
+ render: ({ children, ...args }) => (
55
+ <div className="flex h-24 items-center gap-3">
56
+ <span>Left content</span>
57
+
58
+ <Separator {...args} orientation="vertical" decorative={false} />
59
+
60
+ <span>Right content</span>
61
+ </div>
62
+ ),
63
+ };
@@ -0,0 +1,42 @@
1
+ 'use client';
2
+
3
+ import type { ComponentProps, ReactNode } from 'react';
4
+
5
+ import { Separator as SeparatorPrimitive } from 'radix-ui';
6
+
7
+ import { cn } from '../../utils/components';
8
+ import { Chip } from '../Chip/Chip';
9
+
10
+ type HorizontalSeparatorProps = ComponentProps<typeof SeparatorPrimitive.Root> & {
11
+ orientation?: 'horizontal';
12
+ children?: ReactNode;
13
+ };
14
+
15
+ type VerticalSeparatorProps = ComponentProps<typeof SeparatorPrimitive.Root> & {
16
+ orientation: 'vertical';
17
+ children?: never;
18
+ };
19
+
20
+ export type SeparatorProps = HorizontalSeparatorProps | VerticalSeparatorProps;
21
+
22
+ export const Separator = ({
23
+ className,
24
+ orientation,
25
+ decorative = true,
26
+ children,
27
+ ...props
28
+ }: SeparatorProps) => (
29
+ <div {...props} className={cn('cn-separator', className)} data-orientation={orientation}>
30
+ <SeparatorPrimitive.Root
31
+ data-slot="separator"
32
+ decorative={decorative}
33
+ orientation={orientation}
34
+ className="cn-separator-line"
35
+ />
36
+ {children && (
37
+ <Chip className="cn-separator-content" appearance="neutral" size="sm">
38
+ {children}
39
+ </Chip>
40
+ )}
41
+ </div>
42
+ );
@@ -0,0 +1,118 @@
1
+ 'use client';
2
+
3
+ import { useLayoutEffect, useRef, useState, type ComponentProps } from 'react';
4
+
5
+ import { usePanelRef } from 'react-resizable-panels';
6
+
7
+ import { useSidebar } from './Sidebar';
8
+ import { cn } from '../../utils/components';
9
+ import { ResizableHandle, ResizablePanel } from '../Resizable/Resizable';
10
+
11
+ export type ResizableSidebarProps = Omit<
12
+ ComponentProps<typeof ResizablePanel>,
13
+ 'panelRef' | 'onResize' | 'collapsible'
14
+ >;
15
+
16
+ export const ResizableSidebar = ({
17
+ className,
18
+ defaultSize = '16rem',
19
+ minSize = '12rem',
20
+ collapsedSize = 0,
21
+ ...props
22
+ }: ResizableSidebarProps) => {
23
+ const { open, setOpen } = useSidebar();
24
+ const panelRef = usePanelRef();
25
+ const isFirstRender = useRef(true);
26
+
27
+ // Captured once and never recomputed. Panel's own registration (and the
28
+ // "most recent size" expand() restores) depends on this prop, so passing
29
+ // a value that changes every time `open` toggles corrupts that memory,
30
+ // reopening lands back on this value instead of wherever the user last
31
+ // dragged to before closing.
32
+ const [initialSize] = useState(() => (open ? defaultSize : collapsedSize));
33
+
34
+ // Skip the very first run: ResizableSidebar commits before its parent
35
+ // ResizablePanelGroup finishes registering itself, so calling expand() or
36
+ // collapse() here on mount throws ("Group not found"). defaultSize below
37
+ // already renders the correct initial state, so this effect only needs to
38
+ // handle `open` changing after mount, by which point the group is stable.
39
+ //
40
+ // The only drag-driven `open` change left is closing (handleResize below
41
+ // sets it): ResizableSidebarHandle disables itself while closed, so a drag
42
+ // can never produce the reverse (open) transition. collapse() here is a
43
+ // harmless no-op for that case, the panel's already exactly at
44
+ // collapsedSize by the time the drag triggers it.
45
+ useLayoutEffect(() => {
46
+ if (isFirstRender.current) {
47
+ isFirstRender.current = false;
48
+
49
+ return;
50
+ }
51
+
52
+ // expand()/collapse() throw if this panel's ResizablePanelGroup isn't
53
+ // currently registered (react-resizable-panels keeps a module-level
54
+ // registry, looked up by id on every call, not a stable reference). The
55
+ // isFirstRender guard above only covers this component's own first
56
+ // commit; a Next.js route transition can still unmount/remount the whole
57
+ // group+panel tree, or briefly leave it unregistered, independently of
58
+ // that. There's nothing to sync in that instant, the panel already
59
+ // renders at the correct size once mounted, so swallow it rather than
60
+ // crashing the page.
61
+ try {
62
+ if (open) {
63
+ panelRef.current?.expand();
64
+ } else {
65
+ panelRef.current?.collapse();
66
+ }
67
+ } catch {
68
+ // no-op, see comment above
69
+ }
70
+ }, [open, panelRef]);
71
+
72
+ const handleResize = () => {
73
+ const collapsed = panelRef.current?.isCollapsed() ?? false;
74
+
75
+ // Dragging the handle closed doesn't go through the trigger, so sync
76
+ // `open` back here to keep it (and the trigger button) accurate.
77
+ if (collapsed === open) {
78
+ setOpen(!collapsed);
79
+ }
80
+ };
81
+
82
+ return (
83
+ <ResizablePanel
84
+ panelRef={panelRef}
85
+ collapsible
86
+ defaultSize={initialSize}
87
+ minSize={minSize}
88
+ collapsedSize={collapsedSize}
89
+ onResize={handleResize}
90
+ // Matches Sidebar's own <aside inert={...}>: at 0 width the panel is
91
+ // still reachable by Tab unless explicitly made inert.
92
+ inert={open ? undefined : true}
93
+ className={cn('cn-resizable-sidebar', className)}
94
+ {...props}
95
+ />
96
+ );
97
+ };
98
+
99
+ export type ResizableSidebarHandleProps = ComponentProps<typeof ResizableHandle>;
100
+
101
+ // react-resizable-panels won't drag a collapsible panel open again from a
102
+ // fully collapsed (0) size unless the gesture crosses roughly the halfway
103
+ // point to minSize, and arrow keys move in increments far too small to ever
104
+ // cross it, so a collapsed ResizableSidebar's handle would look broken rather
105
+ // than draggable if it stayed visible. Disabling it hides it entirely (see
106
+ // [data-separator='disabled'] in Resizable.css) and stops it responding to
107
+ // input, while still rendering the same mounted Separator: actually removing
108
+ // it from the tree while closed (an earlier version of this returned null)
109
+ // broke react-resizable-panels' own Group, unmounting/remounting a Separator
110
+ // confuses its internal pointer-listener bookkeeping, throwing
111
+ // "object that is not, or is no longer, usable" the next time you tried to
112
+ // drag anything in the group. `disabled` still lets a consumer force-disable
113
+ // it while open, for reasons unrelated to the collapsed state.
114
+ export const ResizableSidebarHandle = ({ disabled, ...props }: ResizableSidebarHandleProps) => {
115
+ const { open } = useSidebar();
116
+
117
+ return <ResizableHandle disabled={disabled ?? !open} {...props} />;
118
+ };