@tpzdsp/next-toolkit 2.5.1 → 3.1.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 (357) 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 +34 -0
  62. package/src/components/ErrorModal/ErrorModal.stories.tsx +100 -0
  63. package/src/components/ErrorModal/ErrorModal.tsx +123 -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 +3 -2
  77. package/src/components/Footer/Footer.stories.tsx +27 -0
  78. package/src/components/Footer/Footer.tsx +46 -0
  79. package/src/components/Footer/Licence.tsx +18 -0
  80. package/src/components/Footer/MetaLinks.tsx +47 -0
  81. package/src/components/Footer/Small/SmallFooter.stories.tsx +3 -0
  82. package/src/components/Footer/Small/SmallFooter.tsx +53 -41
  83. package/src/components/Form/FormGroup.css +18 -0
  84. package/src/components/Form/FormGroup.stories.tsx +87 -0
  85. package/src/components/Form/FormGroup.tsx +16 -0
  86. package/src/components/{layout/header → Header}/Header.stories.tsx +26 -12
  87. package/src/components/Header/Header.test.tsx +87 -0
  88. package/src/components/Header/Header.tsx +304 -0
  89. package/src/components/Header/HeaderAppContent.tsx +120 -0
  90. package/src/components/Heading/Heading.css +40 -0
  91. package/src/components/Heading/Heading.stories.tsx +15 -5
  92. package/src/components/Heading/Heading.tsx +21 -44
  93. package/src/components/Heading/HeadingSkeleton.stories.tsx +30 -0
  94. package/src/components/Heading/HeadingSkeleton.tsx +14 -0
  95. package/src/components/Hero/Hero.css +19 -0
  96. package/src/components/Hero/Hero.stories.tsx +41 -0
  97. package/src/components/Hero/Hero.tsx +14 -0
  98. package/src/components/HintText/HintText.css +5 -0
  99. package/src/components/HintText/HintText.stories.tsx +29 -0
  100. package/src/components/HintText/HintText.tsx +13 -0
  101. package/src/components/InfoBox/InfoBox.css +38 -0
  102. package/src/components/InfoBox/InfoBox.stories.tsx +51 -403
  103. package/src/components/InfoBox/InfoBox.tsx +31 -158
  104. package/src/components/Input/Input.css +77 -0
  105. package/src/components/Input/Input.stories.tsx +123 -0
  106. package/src/components/Input/Input.tsx +74 -0
  107. package/src/components/InputGroup/InputGroup.css +140 -0
  108. package/src/components/InputGroup/InputGroup.stories.tsx +125 -0
  109. package/src/components/InputGroup/InputGroup.tsx +47 -0
  110. package/src/components/InsetText/InsetText.css +5 -0
  111. package/src/components/InsetText/InsetText.stories.tsx +30 -0
  112. package/src/components/InsetText/InsetText.tsx +9 -0
  113. package/src/components/Keyboard/Keyboard.css +9 -0
  114. package/src/components/Keyboard/Keyboard.stories.tsx +63 -0
  115. package/src/components/Keyboard/Keyboard.tsx +30 -0
  116. package/src/components/Label/Label.css +27 -0
  117. package/src/components/Label/Label.stories.tsx +40 -0
  118. package/src/components/Label/Label.tsx +17 -0
  119. package/src/components/LayerSwitcher/LayerSwitcher.css +59 -0
  120. package/src/components/LayerSwitcher/LayerSwitcher.stories.tsx +112 -0
  121. package/src/components/LayerSwitcher/LayerSwitcher.tsx +61 -0
  122. package/src/components/Layout/401/401.stories.tsx +23 -0
  123. package/src/components/Layout/401/401.tsx +1 -0
  124. package/src/components/Layout/403/403.stories.tsx +23 -0
  125. package/src/components/Layout/403/403.tsx +21 -0
  126. package/src/components/Layout/404/404.stories.tsx +23 -0
  127. package/src/components/Layout/404/404.tsx +28 -0
  128. package/src/components/Layout/500/500.stories.tsx +56 -0
  129. package/src/components/Layout/500/500.tsx +63 -0
  130. package/src/components/Layout/ButtonGroup/ButtonGroup.css +52 -0
  131. package/src/components/Layout/ButtonGroup/ButtonGroup.stories.tsx +156 -0
  132. package/src/components/Layout/ButtonGroup/ButtonGroup.tsx +50 -0
  133. package/src/components/Layout/HeadingGroup/HeadingGroup.css +21 -0
  134. package/src/components/Layout/HeadingGroup/HeadingGroup.stories.tsx +84 -0
  135. package/src/components/Layout/HeadingGroup/HeadingGroup.tsx +9 -0
  136. package/src/components/Layout/MainWrapper/MainWrapper.css +12 -0
  137. package/src/components/Layout/MainWrapper/MainWrapper.stories.tsx +36 -0
  138. package/src/components/Layout/MainWrapper/MainWrapper.tsx +14 -0
  139. package/src/components/Layout/PageShell/PageShell.css +42 -0
  140. package/src/components/Layout/PageShell/PageShell.stories.tsx +108 -0
  141. package/src/components/Layout/PageShell/PageShell.tsx +31 -0
  142. package/src/components/Layout/ParagraphGroup/ParagraphGroup.css +5 -0
  143. package/src/components/Layout/ParagraphGroup/ParagraphGroup.stories.tsx +34 -0
  144. package/src/components/Layout/ParagraphGroup/ParagraphGroup.tsx +9 -0
  145. package/src/components/Layout/Stack/Stack.css +29 -0
  146. package/src/components/Layout/Stack/Stack.stories.tsx +83 -0
  147. package/src/components/Layout/Stack/Stack.tsx +19 -0
  148. package/src/components/Layout/WidthContainer/WidthContainer.css +17 -0
  149. package/src/components/Layout/WidthContainer/WidthContainer.stories.tsx +59 -0
  150. package/src/components/Layout/WidthContainer/WidthContainer.tsx +13 -0
  151. package/src/components/Legend/Legend.css +95 -0
  152. package/src/components/Legend/Legend.stories.tsx +81 -0
  153. package/src/components/Legend/Legend.tsx +32 -0
  154. package/src/components/Legend/LegendDot.tsx +15 -0
  155. package/src/components/Legend/LegendPanel.stories.tsx +101 -0
  156. package/src/components/Legend/LegendPanel.tsx +68 -0
  157. package/src/components/Legend/LegendSkeleton.stories.tsx +87 -0
  158. package/src/components/Legend/LegendSkeleton.tsx +73 -0
  159. package/src/components/LegendSelect/LegendSelect.css +8 -0
  160. package/src/components/LegendSelect/LegendSelect.stories.tsx +80 -0
  161. package/src/components/LegendSelect/LegendSelect.tsx +26 -0
  162. package/src/components/Link/Link.css +98 -0
  163. package/src/components/Link/Link.stories.tsx +70 -0
  164. package/src/components/Link/Link.tsx +52 -0
  165. package/src/components/LinkButton/LinkButton.css +67 -0
  166. package/src/components/LinkButton/LinkButton.stories.tsx +90 -42
  167. package/src/components/LinkButton/LinkButton.tsx +50 -63
  168. package/src/components/LinkList/LinkList.css +28 -0
  169. package/src/components/LinkList/LinkList.stories.tsx +46 -0
  170. package/src/components/LinkList/LinkList.tsx +23 -0
  171. package/src/components/List/Li.tsx +9 -0
  172. package/src/components/List/List.css +9 -0
  173. package/src/components/List/OrderedList.stories.tsx +36 -0
  174. package/src/components/List/OrderedList.tsx +9 -0
  175. package/src/components/List/UnorderedList.stories.tsx +36 -0
  176. package/src/components/List/UnorderedList.tsx +9 -0
  177. package/src/components/LoadingBox/LoadingBox.css +27 -0
  178. package/src/components/LoadingBox/LoadingBox.stories.tsx +66 -0
  179. package/src/components/LoadingBox/LoadingBox.tsx +40 -0
  180. package/src/components/Modal/Modal.css +111 -0
  181. package/src/components/Modal/Modal.stories.tsx +94 -155
  182. package/src/components/Modal/Modal.tsx +141 -75
  183. package/src/components/NoScriptBanner/NoScriptBanner.css +17 -0
  184. package/src/components/NoScriptBanner/NoScriptBanner.stories.tsx +46 -0
  185. package/src/components/NoScriptBanner/NoScriptBanner.tsx +26 -0
  186. package/src/components/Paragraph/Paragraph.css +22 -0
  187. package/src/components/Paragraph/Paragraph.stories.tsx +1 -42
  188. package/src/components/Paragraph/Paragraph.tsx +4 -3
  189. package/src/components/Paragraph/ParagraphSkeleton.stories.tsx +44 -0
  190. package/src/components/Paragraph/ParagraphSkeleton.tsx +33 -0
  191. package/src/components/PasswordInput/PasswordInput.css +3 -0
  192. package/src/components/PasswordInput/PasswordInput.stories.tsx +47 -0
  193. package/src/components/PasswordInput/PasswordInput.tsx +45 -0
  194. package/src/components/PhaseBanner/PhaseBanner.css +12 -0
  195. package/src/components/PhaseBanner/PhaseBanner.stories.tsx +61 -0
  196. package/src/components/PhaseBanner/PhaseBanner.tsx +17 -0
  197. package/src/components/Popover/Popover.css +43 -0
  198. package/src/components/Popover/Popover.stories.tsx +102 -0
  199. package/src/components/Popover/Popover.tsx +40 -0
  200. package/src/components/Radio/Radio.css +69 -0
  201. package/src/components/Radio/Radio.stories.tsx +169 -0
  202. package/src/components/Radio/Radio.tsx +56 -0
  203. package/src/components/Resizable/Resizable.css +86 -0
  204. package/src/components/Resizable/Resizable.stories.tsx +123 -0
  205. package/src/components/Resizable/Resizable.tsx +86 -0
  206. package/src/components/ScrollArea/ScrollArea.css +9 -0
  207. package/src/components/ScrollArea/ScrollArea.stories.tsx +42 -0
  208. package/src/components/ScrollArea/ScrollArea.tsx +11 -0
  209. package/src/components/SearchBox/SearchBox.stories.tsx +88 -0
  210. package/src/components/SearchBox/SearchBox.tsx +36 -0
  211. package/src/components/Select/AsyncSelect.stories.tsx +84 -0
  212. package/src/components/Select/AsyncSelect.tsx +41 -0
  213. package/src/components/Select/Select.css +202 -0
  214. package/src/components/Select/Select.stories.tsx +162 -0
  215. package/src/components/Select/Select.test.tsx +36 -0
  216. package/src/components/Select/Select.tsx +296 -0
  217. package/src/components/Select/SelectSkeleton.stories.tsx +51 -0
  218. package/src/components/Select/SelectSkeleton.tsx +23 -0
  219. package/src/components/Separator/Separator.css +41 -0
  220. package/src/components/Separator/Separator.stories.tsx +63 -0
  221. package/src/components/Separator/Separator.tsx +42 -0
  222. package/src/components/Sidebar/ResizableSidebar.tsx +118 -0
  223. package/src/components/Sidebar/Sidebar.css +203 -0
  224. package/src/components/Sidebar/Sidebar.stories.tsx +438 -0
  225. package/src/components/Sidebar/Sidebar.tsx +225 -0
  226. package/src/components/Skeleton/Skeleton.css +16 -0
  227. package/src/components/Skeleton/Skeleton.stories.tsx +39 -0
  228. package/src/components/Skeleton/Skeleton.tsx +12 -0
  229. package/src/components/SkipLink/SkipLink.css +16 -0
  230. package/src/components/SkipLink/SkipLink.stories.tsx +40 -0
  231. package/src/components/SkipLink/SkipLink.tsx +15 -0
  232. package/src/components/Spinner/Spinner.css +30 -0
  233. package/src/components/Spinner/Spinner.stories.tsx +36 -0
  234. package/src/components/Spinner/Spinner.tsx +45 -0
  235. package/src/components/StartButton/StartButton.css +13 -0
  236. package/src/components/StartButton/StartButton.stories.tsx +68 -0
  237. package/src/components/StartButton/StartButton.tsx +50 -0
  238. package/src/components/SubmitButton/SubmitButton.stories.tsx +31 -0
  239. package/src/components/SubmitButton/SubmitButton.tsx +14 -0
  240. package/src/components/SummaryList/SummaryList.css +71 -0
  241. package/src/components/SummaryList/SummaryList.stories.tsx +211 -0
  242. package/src/components/SummaryList/SummaryList.tsx +41 -0
  243. package/src/components/SummaryList/SummaryListSkeleton.stories.tsx +67 -0
  244. package/src/components/SummaryList/SummaryListSkeleton.tsx +40 -0
  245. package/src/components/Tabs/Tabs.css +84 -0
  246. package/src/components/Tabs/Tabs.stories.tsx +133 -0
  247. package/src/components/Tabs/Tabs.tsx +66 -0
  248. package/src/components/Tag/Tag.css +8 -0
  249. package/src/components/Tag/Tag.stories.tsx +96 -0
  250. package/src/components/Tag/Tag.tsx +34 -0
  251. package/src/components/Textarea/Textarea.css +38 -0
  252. package/src/components/Textarea/Textarea.stories.tsx +131 -0
  253. package/src/components/Textarea/Textarea.tsx +50 -0
  254. package/src/components/TextareaCount/TextareaCount.css +17 -0
  255. package/src/components/TextareaCount/TextareaCount.stories.tsx +131 -0
  256. package/src/components/TextareaCount/TextareaCount.tsx +108 -0
  257. package/src/components/Tooltip/Tooltip.css +43 -0
  258. package/src/components/Tooltip/Tooltip.stories.tsx +110 -0
  259. package/src/components/Tooltip/Tooltip.tsx +53 -0
  260. package/src/components/WarningText/WarningText.css +20 -0
  261. package/src/components/WarningText/WarningText.stories.tsx +29 -0
  262. package/src/components/WarningText/WarningText.tsx +17 -0
  263. package/src/components/index.ts +343 -61
  264. package/src/errors/errorDisplay.ts +70 -0
  265. package/src/errors/index.ts +1 -0
  266. package/src/http/constants.ts +2 -0
  267. package/src/http/fetch.ts +7 -3
  268. package/src/map/LayerSwitcherPanel.tsx +2 -173
  269. package/src/map/LegendShim.tsx +2 -0
  270. package/src/map/MapComponent.tsx +7 -17
  271. package/src/map/MapControlsOverlay.tsx +68 -175
  272. package/src/map/Popup.tsx +71 -24
  273. package/src/map/geocoder/Geocoder.test.tsx +46 -3
  274. package/src/map/geocoder/Geocoder.tsx +155 -278
  275. package/src/map/index.ts +1 -1
  276. package/src/types/utils.ts +0 -10
  277. package/src/utils/components.ts +96 -0
  278. package/src/utils/index.ts +1 -0
  279. package/src/utils/utils.ts +0 -12
  280. package/src/assets/images/defra-logo.svg +0 -51
  281. package/src/assets/images/ea-logo.svg +0 -58
  282. package/src/assets/images/ogl.svg +0 -1
  283. package/src/components/Button/Button.test.tsx +0 -53
  284. package/src/components/ButtonLink/ButtonLink.test.tsx +0 -154
  285. package/src/components/Card/Card.test.tsx +0 -51
  286. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +0 -75
  287. package/src/components/ErrorBoundary/ErrorFallback.test.tsx +0 -107
  288. package/src/components/ErrorText/ErrorText.test.tsx +0 -33
  289. package/src/components/Heading/Heading.test.tsx +0 -22
  290. package/src/components/Hint/Hint.stories.tsx +0 -58
  291. package/src/components/Hint/Hint.test.tsx +0 -33
  292. package/src/components/Hint/Hint.tsx +0 -12
  293. package/src/components/InfoBox/InfoBox.test.tsx +0 -330
  294. package/src/components/InfoBox/types.ts +0 -6
  295. package/src/components/LinkButton/LinkButton.test.tsx +0 -177
  296. package/src/components/Modal/Modal.test.tsx +0 -246
  297. package/src/components/NotificationBanner/NotificationBanner.stories.tsx +0 -45
  298. package/src/components/NotificationBanner/NotificationBanner.test.tsx +0 -58
  299. package/src/components/NotificationBanner/NotificationBanner.tsx +0 -45
  300. package/src/components/Paragraph/Paragraph.test.tsx +0 -10
  301. package/src/components/SlidingPanel/SlidingPanel.stories.tsx +0 -129
  302. package/src/components/SlidingPanel/SlidingPanel.test.tsx +0 -83
  303. package/src/components/SlidingPanel/SlidingPanel.tsx +0 -258
  304. package/src/components/accordion/Accordion.stories.tsx +0 -234
  305. package/src/components/accordion/Accordion.test.tsx +0 -192
  306. package/src/components/accordion/Accordion.tsx +0 -73
  307. package/src/components/backToTop/BackToTop.stories.tsx +0 -407
  308. package/src/components/backToTop/BackToTop.test.tsx +0 -57
  309. package/src/components/backToTop/BackToTop.tsx +0 -134
  310. package/src/components/chip/Chip.stories.tsx +0 -61
  311. package/src/components/chip/Chip.test.tsx +0 -31
  312. package/src/components/chip/Chip.tsx +0 -23
  313. package/src/components/container/Container.tsx +0 -42
  314. package/src/components/cookieBanner/CookieBanner.stories.tsx +0 -257
  315. package/src/components/cookieBanner/CookieBanner.test.tsx +0 -68
  316. package/src/components/cookieBanner/CookieBanner.tsx +0 -73
  317. package/src/components/divider/RuleDivider.stories.tsx +0 -254
  318. package/src/components/divider/RuleDivider.test.tsx +0 -164
  319. package/src/components/divider/RuleDivider.tsx +0 -23
  320. package/src/components/dropdown/DropdownMenu.test.tsx +0 -211
  321. package/src/components/dropdown/DropdownMenu.tsx +0 -108
  322. package/src/components/dropdown/useDropdownMenu.ts +0 -249
  323. package/src/components/form/Input.stories.tsx +0 -435
  324. package/src/components/form/Input.test.tsx +0 -206
  325. package/src/components/form/Input.tsx +0 -23
  326. package/src/components/form/TextArea.stories.tsx +0 -464
  327. package/src/components/form/TextArea.test.tsx +0 -232
  328. package/src/components/form/TextArea.tsx +0 -23
  329. package/src/components/layout/footer/Footer.tsx +0 -26
  330. package/src/components/layout/footer/Licence.tsx +0 -19
  331. package/src/components/layout/footer/MetaLinks.tsx +0 -36
  332. package/src/components/layout/header/Header.test.tsx +0 -36
  333. package/src/components/layout/header/Header.tsx +0 -90
  334. package/src/components/layout/header/HeaderAuthClient.test.tsx +0 -45
  335. package/src/components/layout/header/HeaderAuthClient.tsx +0 -40
  336. package/src/components/layout/header/HeaderNavClient.test.tsx +0 -44
  337. package/src/components/layout/header/HeaderNavClient.tsx +0 -67
  338. package/src/components/link/ExternalLink.test.tsx +0 -102
  339. package/src/components/link/ExternalLink.tsx +0 -29
  340. package/src/components/link/Link.tsx +0 -26
  341. package/src/components/select/Select.stories.tsx +0 -336
  342. package/src/components/select/Select.test.tsx +0 -474
  343. package/src/components/select/Select.tsx +0 -125
  344. package/src/components/select/SelectSkeleton.stories.tsx +0 -194
  345. package/src/components/select/SelectSkeleton.test.tsx +0 -104
  346. package/src/components/select/SelectSkeleton.tsx +0 -23
  347. package/src/components/select/common.ts +0 -3
  348. package/src/components/select/index.ts +0 -18
  349. package/src/components/skipLink/SkipLink.stories.tsx +0 -346
  350. package/src/components/skipLink/SkipLink.test.tsx +0 -22
  351. package/src/components/skipLink/SkipLink.tsx +0 -49
  352. package/src/map/LegendPanel.tsx +0 -85
  353. /package/src/{components → assets}/images/DefraLogo.tsx +0 -0
  354. /package/src/{components → assets}/images/EaLogo.tsx +0 -0
  355. /package/src/{components → assets}/images/OglLogo.tsx +0 -0
  356. /package/src/components/{googleAnalytics → GoogleAnalytics}/GlobalVars.tsx +0 -0
  357. /package/src/components/{googleAnalytics → GoogleAnalytics}/GoogleAnalytics.tsx +0 -0
@@ -0,0 +1,296 @@
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) =>
85
+ cn(
86
+ 'cn-select-menu',
87
+ props.placement === 'top' && 'cn-select-menu-top',
88
+ userClassNames?.menu?.(props),
89
+ ),
90
+ menuList: (props) => cn('cn-select-menu-list', userClassNames?.menuList?.(props)),
91
+ option: (props) =>
92
+ cn(
93
+ 'cn-select-option',
94
+ props.isFocused && !props.isSelected && 'cn-select-option-focused',
95
+ props.isSelected && 'cn-select-option-selected',
96
+ props.isDisabled && 'cn-select-option-disabled',
97
+ userClassNames?.option?.(props),
98
+ ),
99
+ noOptionsMessage: (props) =>
100
+ cn('cn-select-no-options', userClassNames?.noOptionsMessage?.(props)),
101
+ multiValue: (props) => cn('cn-select-multi-value', userClassNames?.multiValue?.(props)),
102
+ multiValueLabel: (props) =>
103
+ cn('cn-select-multi-value-label', userClassNames?.multiValueLabel?.(props)),
104
+ multiValueRemove: (props) =>
105
+ cn('cn-select-multi-value-remove', userClassNames?.multiValueRemove?.(props)),
106
+ group: (props) => cn('cn-select-group', userClassNames?.group?.(props)),
107
+ groupHeading: (props) => cn('cn-select-group-heading', userClassNames?.groupHeading?.(props)),
108
+ });
109
+
110
+ const DropdownIndicator = <Option, IsMulti extends boolean, Group extends GroupBase<Option>>(
111
+ props: DropdownIndicatorProps<Option, IsMulti, Group>,
112
+ ) => (
113
+ <LuChevronDown
114
+ className={props.getClassNames('dropdownIndicator', props)}
115
+ strokeWidth={2.5}
116
+ aria-hidden
117
+ />
118
+ );
119
+
120
+ const ClearIndicator = <Option, IsMulti extends boolean, Group extends GroupBase<Option>>(
121
+ props: ClearIndicatorProps<Option, IsMulti, Group>,
122
+ ) => (
123
+ <components.ClearIndicator {...props}>
124
+ <LuX className="w-full h-full" strokeWidth={2.5} aria-hidden />
125
+ </components.ClearIndicator>
126
+ );
127
+
128
+ const LoadingIndicator = <Option, IsMulti extends boolean, Group extends GroupBase<Option>>(
129
+ props: LoadingIndicatorProps<Option, IsMulti, Group>,
130
+ ) => (
131
+ <LuLoaderCircle
132
+ className={cn(props.getClassNames('loadingIndicator', props), 'animate-spin')}
133
+ aria-hidden
134
+ />
135
+ );
136
+
137
+ // react-select's own renderInput() computes its own aria-describedby (pointing
138
+ // at its internal live-region or placeholder element) and spreads it onto the
139
+ // real <input> last, after any of its own props, so it always wins over
140
+ // whatever Select was originally given, see SelectProps['aria-describedby'].
141
+ // selectProps is react-select's own full, unfiltered instance props object
142
+ // though, still holding that original untouched value, so it survives there
143
+ // and can be merged back in, just needs a cast, react-select's own Props type
144
+ // doesn't declare aria-describedby at all (unlike its aria-invalid/label/etc,
145
+ // which it does forward as-is, no merging needed for those).
146
+ const Input = <Option, IsMulti extends boolean, Group extends GroupBase<Option>>(
147
+ props: InputProps<Option, IsMulti, Group>,
148
+ ) => {
149
+ const external = (props.selectProps as { 'aria-describedby'?: string })['aria-describedby'];
150
+ const ariaDescribedBy = [props['aria-describedby'], external].filter(Boolean).join(' ');
151
+
152
+ return <components.Input {...props} aria-describedby={ariaDescribedBy || undefined} />;
153
+ };
154
+
155
+ const GroupHeading = <Option, IsMulti extends boolean, Group extends GroupBase<Option>>(
156
+ props: GroupHeadingProps<Option, IsMulti, Group>,
157
+ ) => <Separator className={props.getClassNames('groupHeading', props)}>{props.children}</Separator>;
158
+
159
+ const MultiValue = <Option, IsMulti extends boolean, Group extends GroupBase<Option>>({
160
+ removeProps,
161
+ isDisabled,
162
+ selectProps,
163
+ data,
164
+ }: MultiValueProps<Option, IsMulti, Group>) => {
165
+ const label = selectProps.getOptionLabel(data);
166
+
167
+ return (
168
+ <button
169
+ type="button"
170
+ className="cn-select-multi-value"
171
+ aria-label={`Remove ${label}`}
172
+ disabled={isDisabled}
173
+ onClick={removeProps.onClick as unknown as React.MouseEventHandler<HTMLButtonElement>}
174
+ onMouseDown={removeProps.onMouseDown as unknown as React.MouseEventHandler<HTMLButtonElement>}
175
+ onTouchEnd={removeProps.onTouchEnd as unknown as React.TouchEventHandler<HTMLButtonElement>}
176
+ >
177
+ {label}
178
+ <LuX aria-hidden className="cn-select-multi-value-remove" strokeWidth={2.5} />
179
+ </button>
180
+ );
181
+ };
182
+
183
+ const HiddenMultiValue = () => null;
184
+
185
+ const HiddenLoadingIndicator = () => null;
186
+
187
+ const SelectContainerWithTagsBelow = <
188
+ Option,
189
+ IsMulti extends boolean,
190
+ Group extends GroupBase<Option>,
191
+ >(
192
+ props: ContainerProps<Option, IsMulti, Group>,
193
+ ) => {
194
+ const { children, selectProps, getValue, hasValue } = props;
195
+ const { getOptionLabel, getOptionValue, onChange, name, isDisabled } = selectProps;
196
+
197
+ const handleRemove = (option: Option) => {
198
+ const candidate = getOptionValue(option);
199
+ const newValue = getValue().filter((item) => getOptionValue(item) !== candidate);
200
+
201
+ onChange(newValue as OnChangeValue<Option, IsMulti>, {
202
+ action: 'remove-value',
203
+ removedValue: option,
204
+ name,
205
+ });
206
+ };
207
+
208
+ return (
209
+ <components.SelectContainer {...props}>
210
+ {children}
211
+
212
+ {hasValue ? (
213
+ <div className="cn-select-tags">
214
+ {getValue().map((option) => (
215
+ <button
216
+ key={getOptionValue(option)}
217
+ type="button"
218
+ className="cn-select-tag"
219
+ aria-label={`Remove ${getOptionLabel(option)}`}
220
+ disabled={isDisabled}
221
+ onClick={() => handleRemove(option)}
222
+ >
223
+ {getOptionLabel(option)}
224
+ <LuX aria-hidden className="cn-select-multi-value-remove" strokeWidth={2.5} />
225
+ </button>
226
+ ))}
227
+ </div>
228
+ ) : null}
229
+ </components.SelectContainer>
230
+ );
231
+ };
232
+
233
+ export const Select = <
234
+ Option = unknown,
235
+ IsMulti extends boolean = false,
236
+ Group extends GroupBase<Option> = GroupBase<Option>,
237
+ >({
238
+ classNames: userClassNames,
239
+ styles: userStyles,
240
+ invalid = false,
241
+ isMulti,
242
+ tagsPlacement = 'inline',
243
+ hideLoadingIndicator = false,
244
+ ...props
245
+ }: SelectProps<Option, IsMulti, Group>) => {
246
+ const instanceId = useId();
247
+ const showTagsBelow = isMulti && tagsPlacement === 'below';
248
+
249
+ return (
250
+ <ReactSelect
251
+ menuPortalTarget={typeof document !== 'undefined' ? document.body : null}
252
+ menuPosition="fixed"
253
+ tabSelectsValue={false}
254
+ {...props}
255
+ isMulti={isMulti}
256
+ instanceId={instanceId}
257
+ unstyled
258
+ aria-invalid={invalid ? 'true' : undefined}
259
+ styles={{
260
+ ...userStyles,
261
+ // react-select injects outline: 0 !important and transition via Emotion
262
+ // on the control div even in unstyled mode. By returning an empty object
263
+ // (not spreading base) we prevent that class from being generated, so
264
+ // our CSS classes can set outline freely without needing !important.
265
+ control: (_, controlProps) => ({
266
+ ...userStyles?.control?.({}, controlProps),
267
+ }),
268
+ // Removes the `pointer-events: none` style.
269
+ container: (_, containerProps) => ({
270
+ ...userStyles?.container?.({}, containerProps),
271
+ }),
272
+ // react-select renders the portal wrapper itself with an inline
273
+ // style, a separate element from .cn-select-menu (which already
274
+ // has cn-z-overlay), so it isn't reachable via our own CSS classes
275
+ // and has to independently match that same value (999999) here, or
276
+ // this outer wrapper's z-index is what actually determines stacking
277
+ // against unrelated high z-index content elsewhere on the page.
278
+ menuPortal: (base, portalProps) => ({
279
+ ...base,
280
+ zIndex: 999999,
281
+ ...userStyles?.menuPortal?.(base, portalProps),
282
+ }),
283
+ }}
284
+ classNames={buildClassNames<Option, IsMulti, Group>(invalid, userClassNames)}
285
+ components={{
286
+ Input,
287
+ DropdownIndicator,
288
+ ClearIndicator,
289
+ LoadingIndicator: hideLoadingIndicator ? HiddenLoadingIndicator : LoadingIndicator,
290
+ MultiValue: showTagsBelow ? HiddenMultiValue : MultiValue,
291
+ GroupHeading,
292
+ ...(showTagsBelow ? { SelectContainer: SelectContainerWithTagsBelow } : {}),
293
+ }}
294
+ />
295
+ );
296
+ };
@@ -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
+ };