stargazer-ui 1.5.12 → 1.5.13

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 (371) hide show
  1. package/.babelrc.json +10 -0
  2. package/.eslintrc.cjs +18 -0
  3. package/.gitattributes +2 -0
  4. package/LICENSE +21 -0
  5. package/dev/index.html +12 -0
  6. package/dev/index.jsx +46 -0
  7. package/dev/index.scss +74 -0
  8. package/dev/pages/ButtonPage.jsx +44 -0
  9. package/dev/pages/CardPage.jsx +81 -0
  10. package/dev/pages/DropdownPage.jsx +32 -0
  11. package/dev/pages/FormPage.jsx +155 -0
  12. package/dev/pages/ListPage.jsx +52 -0
  13. package/dev/pages/ModalPage.jsx +38 -0
  14. package/dev/pages/OverlayPage.jsx +39 -0
  15. package/dev/pages/components.jsx +19 -0
  16. package/dev/stargazerui.css +3762 -0
  17. package/dev/stargazerui.css.map +1 -0
  18. package/dev/test.jsx +87 -0
  19. package/package.json +79 -1
  20. package/rollup.config.js +140 -0
  21. package/scripts/writePackageJsons.js +42 -0
  22. package/src/Bar/Bar.tsx +0 -0
  23. package/src/Bar/Bar.type.ts +9 -0
  24. package/src/Bar/index.ts +0 -0
  25. package/src/Button/Button.tsx +20 -0
  26. package/src/Button/Button.types.ts +8 -0
  27. package/src/Button/index.ts +3 -0
  28. package/src/ButtonGroup/ButtonGroup.tsx +14 -0
  29. package/src/ButtonGroup/ButtonGroup.types.ts +8 -0
  30. package/src/ButtonGroup/index.ts +3 -0
  31. package/src/Card/Card.tsx +70 -0
  32. package/src/Card/Card.types.ts +33 -0
  33. package/src/Card/index.ts +3 -0
  34. package/src/CloseButton/CloseButton.tsx +14 -0
  35. package/src/CloseButton/CloseButton.types.ts +6 -0
  36. package/src/CloseButton/index.ts +3 -0
  37. package/src/Dropdown/Dropdown.tsx +440 -0
  38. package/src/Dropdown/Dropdown.types.ts +60 -0
  39. package/src/Dropdown/DropdownOld.tsx +409 -0
  40. package/src/Dropdown/index.ts +4 -0
  41. package/src/FileUploadButton/FileUploadButton.tsx +27 -0
  42. package/src/FileUploadButton/FileUploadButton.types.ts +9 -0
  43. package/src/FileUploadButton/index.ts +3 -0
  44. package/src/FloatingLabel/FloatingLabel.tsx +22 -0
  45. package/src/FloatingLabel/FloatingLabel.types.ts +11 -0
  46. package/src/FloatingLabel/index.ts +3 -0
  47. package/src/Form/Form.tsx +398 -0
  48. package/src/Form/Form.types.ts +169 -0
  49. package/src/Form/FormSelect.tsx +527 -0
  50. package/src/Form/index.ts +4 -0
  51. package/src/InputGroup/InputGroup.tsx +46 -0
  52. package/src/InputGroup/InputGroup.types.ts +22 -0
  53. package/src/InputGroup/index.ts +4 -0
  54. package/src/List/List.tsx +112 -0
  55. package/src/List/List.types.ts +34 -0
  56. package/src/List/index.ts +4 -0
  57. package/src/Modal/Modal.tsx +229 -0
  58. package/src/Modal/Modal.types.ts +49 -0
  59. package/src/Modal/index.ts +4 -0
  60. package/src/Nav/Nav.tsx +43 -0
  61. package/src/Nav/Nav.types.ts +21 -0
  62. package/src/Nav/index.ts +4 -0
  63. package/src/NavBar/Navbar.tsx +57 -0
  64. package/src/NavBar/Navbar.types.ts +24 -0
  65. package/src/NavBar/index.ts +4 -0
  66. package/src/NavDropdown/NavDropdown.tsx +93 -0
  67. package/src/NavDropdown/NavDropdown.types.ts +6 -0
  68. package/src/NavDropdown/index.ts +3 -0
  69. package/src/Overlay/Overlay.tsx +309 -0
  70. package/src/Overlay/Overlay.types.ts +24 -0
  71. package/{Overlay/index.d.ts → src/Overlay/index.ts} +3 -3
  72. package/src/Popout/Popout.tsx +155 -0
  73. package/src/Popout/Popout.types.ts +42 -0
  74. package/src/Popout/index.ts +3 -0
  75. package/src/Spinner/Spinner.tsx +15 -0
  76. package/src/Spinner/Spinner.types.ts +7 -0
  77. package/src/Spinner/index.ts +3 -0
  78. package/src/Table/Table.tsx +16 -0
  79. package/src/Table/Table.types.ts +9 -0
  80. package/src/Table/index.ts +3 -0
  81. package/src/Tabs/Tabs.tsx +233 -0
  82. package/src/Tabs/Tabs.types.ts +52 -0
  83. package/src/Tabs/index.ts +3 -0
  84. package/src/ToggleButton/ToggleButton.tsx +21 -0
  85. package/src/ToggleButton/ToggleButton.types.ts +8 -0
  86. package/src/ToggleButton/index.ts +3 -0
  87. package/src/assets/tooltip-pointer.svg +3 -0
  88. package/src/assets/warning.svg +39 -0
  89. package/{hooks/index.d.ts → src/hooks/index.ts} +7 -6
  90. package/src/hooks/useClassname.ts +5 -0
  91. package/src/hooks/useDraggable.ts +186 -0
  92. package/src/hooks/useKeepElementFocused.ts +37 -0
  93. package/src/hooks/useQueryParams.ts +12 -0
  94. package/src/hooks/useScreenSize.ts +24 -0
  95. package/src/index.ts +21 -0
  96. package/src/styles/_Card.scss +166 -0
  97. package/src/styles/_CloseButton.scss +51 -0
  98. package/src/styles/_CustomButton.scss +134 -0
  99. package/src/styles/_Dropdown.scss +127 -0
  100. package/src/styles/_FloatingLabel.scss +56 -0
  101. package/src/styles/_Forms.scss +7 -0
  102. package/src/styles/_Grid.scss +178 -0
  103. package/src/styles/_InputGroup.scss +71 -0
  104. package/src/styles/_List.scss +62 -0
  105. package/src/styles/_Modal.scss +234 -0
  106. package/src/styles/_ModalOld.scss +242 -0
  107. package/src/styles/_Nav.scss +36 -0
  108. package/src/styles/_NavBar.scss +116 -0
  109. package/src/styles/_NavDropdown.scss +33 -0
  110. package/src/styles/_OffCanvas.scss +260 -0
  111. package/src/styles/_OverLay.scss +64 -0
  112. package/src/styles/_Popout.scss +75 -0
  113. package/src/styles/_Spinner.scss +19 -0
  114. package/src/styles/_Table.scss +34 -0
  115. package/src/styles/_Tabs.scss +129 -0
  116. package/src/styles/_colors.scss +510 -0
  117. package/src/styles/_components.scss +40 -0
  118. package/src/styles/_functions.scss +134 -0
  119. package/src/styles/_mixins.scss +26 -0
  120. package/src/styles/_reset.scss +237 -0
  121. package/src/styles/_utilities.scss +2480 -0
  122. package/src/styles/_variables.scss +164 -0
  123. package/src/styles/forms/_FormCheck.scss +270 -0
  124. package/src/styles/forms/_FormControl.scss +135 -0
  125. package/src/styles/forms/_FormGroup.scss +26 -0
  126. package/src/styles/forms/_FormLabel.scss +3 -0
  127. package/src/styles/forms/_FormSelect.scss +196 -0
  128. package/src/styles/forms/_FormSlider.scss +116 -0
  129. package/src/styles/forms/_FormText.scss +6 -0
  130. package/{utils/BaseTypes.d.ts → src/utils/BaseTypes.ts} +32 -25
  131. package/src/utils/ContrastingColor.ts +25 -0
  132. package/src/utils/CreateSyntheticEvent.ts +30 -0
  133. package/src/utils/FileImportExport.js +50 -0
  134. package/src/utils/IsInputKey.ts +18 -0
  135. package/src/utils/MergeClassnames.ts +5 -0
  136. package/src/utils/MergeRefs.ts +12 -0
  137. package/src/vite-env.d.ts +1 -0
  138. package/tsconfig-build.json +4 -0
  139. package/tsconfig.json +79 -0
  140. package/tsconfig.node.json +10 -0
  141. package/types/BaseTypes.d.ts +19 -0
  142. package/{Button → types/components/Button}/Button.types.d.ts +2 -1
  143. package/types/components/Button/index.d.ts +1 -0
  144. package/{Card → types/components/Card}/Card.types.d.ts +1 -3
  145. package/types/components/Card/index.d.ts +1 -0
  146. package/{CloseButton → types/components/CloseButton}/CloseButton.types.d.ts +1 -1
  147. package/types/components/CloseButton/index.d.ts +1 -0
  148. package/{Dropdown/DropdownOld.d.ts → types/components/Dropdown/Dropdown.d.ts} +3 -67
  149. package/{Dropdown → types/components/Dropdown}/Dropdown.types.d.ts +11 -12
  150. package/types/components/Dropdown/index.d.ts +1 -0
  151. package/{FloatingLabel → types/components/FloatingLabel}/FloatingLabel.types.d.ts +1 -1
  152. package/types/components/FloatingLabel/index.d.ts +1 -0
  153. package/types/components/Form/Form.d.ts +17 -0
  154. package/types/components/Form/Form.types.d.ts +50 -0
  155. package/types/components/Form/index.d.ts +1 -0
  156. package/types/components/InputGroup/InputGroup.d.ts +6 -0
  157. package/types/components/InputGroup/InputGroup.types.d.ts +10 -0
  158. package/types/components/InputGroup/index.d.ts +1 -0
  159. package/{Modal → types/components/Modal}/Modal.d.ts +1 -1
  160. package/{Modal → types/components/Modal}/Modal.types.d.ts +2 -3
  161. package/types/components/Modal/index.d.ts +1 -0
  162. package/{Nav → types/components/Nav}/Nav.types.d.ts +1 -1
  163. package/types/components/Nav/index.d.ts +1 -0
  164. package/{NavBar → types/components/NavBar}/Navbar.d.ts +2 -1
  165. package/{NavBar → types/components/NavBar}/Navbar.types.d.ts +1 -2
  166. package/types/components/NavBar/index.d.ts +1 -0
  167. package/types/components/NavDropdown/NavDropdown.d.ts +35 -0
  168. package/types/components/NavDropdown/index.d.ts +1 -0
  169. package/{Popout → types/components/Popout}/Popout.types.d.ts +1 -1
  170. package/types/components/Popout/index.d.ts +1 -0
  171. package/types/components/Spinner/index.d.ts +1 -0
  172. package/{Table → types/components/Table}/Table.types.d.ts +1 -1
  173. package/types/components/Table/index.d.ts +1 -0
  174. package/{Tabs → types/components/Tabs}/Tabs.types.d.ts +1 -12
  175. package/types/components/Tabs/index.d.ts +1 -0
  176. package/types/components/ToggleButton/ToggleButton.d.ts +9 -0
  177. package/types/components/ToggleButton/ToggleButton.types.d.ts +0 -0
  178. package/types/components/ToggleButton/index.d.ts +1 -0
  179. package/types/components/index.d.ts +16 -0
  180. package/types/index.d.ts +1 -0
  181. package/vite.config.js +57 -0
  182. package/vite.config.js.timestamp-1708777378490-e94428ceb2bf9.mjs +42 -0
  183. package/Bar/Bar.type.d.ts +0 -6
  184. package/Bar/index.js +0 -2
  185. package/Bar/index.js.map +0 -1
  186. package/Bar/package.json +0 -1
  187. package/Button/Button.js +0 -15
  188. package/Button/Button.js.map +0 -1
  189. package/Button/index.d.ts +0 -3
  190. package/Button/index.js +0 -7
  191. package/Button/index.js.map +0 -1
  192. package/Button/package.json +0 -1
  193. package/ButtonGroup/ButtonGroup.d.ts +0 -4
  194. package/ButtonGroup/ButtonGroup.js +0 -11
  195. package/ButtonGroup/ButtonGroup.js.map +0 -1
  196. package/ButtonGroup/ButtonGroup.types.d.ts +0 -7
  197. package/ButtonGroup/index.d.ts +0 -3
  198. package/ButtonGroup/index.js +0 -7
  199. package/ButtonGroup/index.js.map +0 -1
  200. package/ButtonGroup/package.json +0 -1
  201. package/Card/Card.js +0 -42
  202. package/Card/Card.js.map +0 -1
  203. package/Card/index.d.ts +0 -3
  204. package/Card/index.js +0 -7
  205. package/Card/index.js.map +0 -1
  206. package/Card/package.json +0 -1
  207. package/CloseButton/CloseButton.js +0 -11
  208. package/CloseButton/CloseButton.js.map +0 -1
  209. package/CloseButton/index.d.ts +0 -3
  210. package/CloseButton/index.js +0 -7
  211. package/CloseButton/index.js.map +0 -1
  212. package/CloseButton/package.json +0 -1
  213. package/Dropdown/Dropdown.d.ts +0 -135
  214. package/Dropdown/Dropdown.js +0 -377
  215. package/Dropdown/Dropdown.js.map +0 -1
  216. package/Dropdown/index.d.ts +0 -4
  217. package/Dropdown/index.js +0 -8
  218. package/Dropdown/index.js.map +0 -1
  219. package/Dropdown/package.json +0 -1
  220. package/FileUploadButton/FileUploadButton.d.ts +0 -4
  221. package/FileUploadButton/FileUploadButton.js +0 -20
  222. package/FileUploadButton/FileUploadButton.js.map +0 -1
  223. package/FileUploadButton/FileUploadButton.types.d.ts +0 -7
  224. package/FileUploadButton/index.d.ts +0 -3
  225. package/FileUploadButton/index.js +0 -7
  226. package/FileUploadButton/index.js.map +0 -1
  227. package/FileUploadButton/package.json +0 -1
  228. package/FloatingLabel/FloatingLabel.js +0 -15
  229. package/FloatingLabel/FloatingLabel.js.map +0 -1
  230. package/FloatingLabel/index.d.ts +0 -3
  231. package/FloatingLabel/index.js +0 -7
  232. package/FloatingLabel/index.js.map +0 -1
  233. package/FloatingLabel/package.json +0 -1
  234. package/Form/Form.d.ts +0 -38
  235. package/Form/Form.js +0 -227
  236. package/Form/Form.js.map +0 -1
  237. package/Form/Form.types.d.ts +0 -159
  238. package/Form/FormSelect.d.ts +0 -15
  239. package/Form/FormSelect.js +0 -436
  240. package/Form/FormSelect.js.map +0 -1
  241. package/Form/index.d.ts +0 -4
  242. package/Form/index.js +0 -8
  243. package/Form/index.js.map +0 -1
  244. package/Form/package.json +0 -1
  245. package/InputGroup/InputGroup.d.ts +0 -7
  246. package/InputGroup/InputGroup.js +0 -31
  247. package/InputGroup/InputGroup.js.map +0 -1
  248. package/InputGroup/InputGroup.types.d.ts +0 -17
  249. package/InputGroup/index.d.ts +0 -4
  250. package/InputGroup/index.js +0 -7
  251. package/InputGroup/index.js.map +0 -1
  252. package/InputGroup/package.json +0 -1
  253. package/List/List.d.ts +0 -14
  254. package/List/List.js +0 -77
  255. package/List/List.js.map +0 -1
  256. package/List/List.types.d.ts +0 -28
  257. package/List/index.d.ts +0 -3
  258. package/List/index.js +0 -7
  259. package/List/index.js.map +0 -1
  260. package/List/package.json +0 -1
  261. package/Modal/Modal.js +0 -157
  262. package/Modal/Modal.js.map +0 -1
  263. package/Modal/index.d.ts +0 -3
  264. package/Modal/index.js +0 -7
  265. package/Modal/index.js.map +0 -1
  266. package/Modal/package.json +0 -1
  267. package/Nav/Nav.js +0 -29
  268. package/Nav/Nav.js.map +0 -1
  269. package/Nav/index.d.ts +0 -4
  270. package/Nav/index.js +0 -7
  271. package/Nav/index.js.map +0 -1
  272. package/Nav/package.json +0 -1
  273. package/NavBar/Navbar.js +0 -36
  274. package/NavBar/Navbar.js.map +0 -1
  275. package/NavBar/index.d.ts +0 -4
  276. package/NavBar/index.js +0 -8
  277. package/NavBar/index.js.map +0 -1
  278. package/NavBar/package.json +0 -1
  279. package/NavDropdown/NavDropdown.d.ts +0 -99
  280. package/NavDropdown/NavDropdown.js +0 -75
  281. package/NavDropdown/NavDropdown.js.map +0 -1
  282. package/NavDropdown/index.d.ts +0 -3
  283. package/NavDropdown/index.js +0 -7
  284. package/NavDropdown/index.js.map +0 -1
  285. package/NavDropdown/package.json +0 -1
  286. package/Overlay/Overlay.d.ts +0 -4
  287. package/Overlay/Overlay.js +0 -242
  288. package/Overlay/Overlay.js.map +0 -1
  289. package/Overlay/Overlay.types.d.ts +0 -22
  290. package/Overlay/index.js +0 -7
  291. package/Overlay/index.js.map +0 -1
  292. package/Overlay/package.json +0 -1
  293. package/Popout/Popout.js +0 -111
  294. package/Popout/Popout.js.map +0 -1
  295. package/Popout/index.d.ts +0 -3
  296. package/Popout/index.js +0 -7
  297. package/Popout/index.js.map +0 -1
  298. package/Popout/package.json +0 -1
  299. package/Spinner/Spinner.js +0 -11
  300. package/Spinner/Spinner.js.map +0 -1
  301. package/Spinner/index.d.ts +0 -3
  302. package/Spinner/index.js +0 -7
  303. package/Spinner/index.js.map +0 -1
  304. package/Spinner/package.json +0 -1
  305. package/Table/Table.js +0 -12
  306. package/Table/Table.js.map +0 -1
  307. package/Table/index.d.ts +0 -3
  308. package/Table/index.js +0 -7
  309. package/Table/index.js.map +0 -1
  310. package/Table/package.json +0 -1
  311. package/Tabs/Tabs.js +0 -162
  312. package/Tabs/Tabs.js.map +0 -1
  313. package/Tabs/index.d.ts +0 -3
  314. package/Tabs/index.js +0 -7
  315. package/Tabs/index.js.map +0 -1
  316. package/Tabs/package.json +0 -1
  317. package/ToggleButton/ToggleButton.d.ts +0 -4
  318. package/ToggleButton/ToggleButton.js +0 -18
  319. package/ToggleButton/ToggleButton.js.map +0 -1
  320. package/ToggleButton/ToggleButton.types.d.ts +0 -7
  321. package/ToggleButton/index.d.ts +0 -3
  322. package/ToggleButton/index.js +0 -7
  323. package/ToggleButton/index.js.map +0 -1
  324. package/ToggleButton/package.json +0 -1
  325. package/hooks/index.js +0 -7
  326. package/hooks/index.js.map +0 -1
  327. package/hooks/package.json +0 -1
  328. package/hooks/useClassname.d.ts +0 -2
  329. package/hooks/useClassname.js +0 -7
  330. package/hooks/useClassname.js.map +0 -1
  331. package/hooks/useDraggable.d.ts +0 -23
  332. package/hooks/useDraggable.js +0 -147
  333. package/hooks/useDraggable.js.map +0 -1
  334. package/hooks/useKeepElementFocused.d.ts +0 -2
  335. package/hooks/useKeepElementFocused.js +0 -37
  336. package/hooks/useKeepElementFocused.js.map +0 -1
  337. package/hooks/useQueryParams.d.ts +0 -2
  338. package/hooks/useQueryParams.js +0 -13
  339. package/hooks/useQueryParams.js.map +0 -1
  340. package/hooks/useScreenSize.d.ts +0 -5
  341. package/hooks/useScreenSize.js +0 -21
  342. package/hooks/useScreenSize.js.map +0 -1
  343. package/index.d.ts +0 -18
  344. package/index.js +0 -19
  345. package/index.js.map +0 -1
  346. package/styles/stargazerui.css +0 -6552
  347. package/styles/stargazerui.css.map +0 -1
  348. package/utils/ContrastingColor.d.ts +0 -1
  349. package/utils/CreateSyntheticEvent.d.ts +0 -3
  350. package/utils/CreateSyntheticEvent.js +0 -33
  351. package/utils/CreateSyntheticEvent.js.map +0 -1
  352. package/utils/IsInputKey.d.ts +0 -7
  353. package/utils/IsInputKey.js +0 -20
  354. package/utils/IsInputKey.js.map +0 -1
  355. package/utils/MergeClassnames.d.ts +0 -2
  356. package/utils/MergeClassnames.js +0 -7
  357. package/utils/MergeClassnames.js.map +0 -1
  358. package/utils/MergeRefs.d.ts +0 -2
  359. package/utils/MergeRefs.js +0 -16
  360. package/utils/MergeRefs.js.map +0 -1
  361. /package/{Button → types/components/Button}/Button.d.ts +0 -0
  362. /package/{Card → types/components/Card}/Card.d.ts +0 -0
  363. /package/{CloseButton → types/components/CloseButton}/CloseButton.d.ts +0 -0
  364. /package/{FloatingLabel → types/components/FloatingLabel}/FloatingLabel.d.ts +0 -0
  365. /package/{Nav → types/components/Nav}/Nav.d.ts +0 -0
  366. /package/{NavDropdown → types/components/NavDropdown}/NavDropdown.types.d.ts +0 -0
  367. /package/{Popout → types/components/Popout}/Popout.d.ts +0 -0
  368. /package/{Spinner → types/components/Spinner}/Spinner.d.ts +0 -0
  369. /package/{Spinner → types/components/Spinner}/Spinner.types.d.ts +0 -0
  370. /package/{Table → types/components/Table}/Table.d.ts +0 -0
  371. /package/{Tabs → types/components/Tabs}/Tabs.d.ts +0 -0
@@ -0,0 +1,16 @@
1
+ import React, { forwardRef } from "react";
2
+
3
+ import { TableType } from "./Table.types";
4
+
5
+
6
+ const Table = forwardRef<HTMLTableElement, TableType>( ({children, size="lg", className, ...restProps}, ref) => {
7
+ let classesComputed = `sg-table${className ? " "+className:""}${size === "sm" ? " "+"sg-table-sm":""}`
8
+ return (
9
+ <table ref={ref} className={classesComputed} {...restProps}>
10
+ {children}
11
+ </table>
12
+ )
13
+ })
14
+ Table.displayName = "Table"
15
+
16
+ export default Table
@@ -0,0 +1,9 @@
1
+ import { ReactNode } from "react";
2
+
3
+ import { BaseTableType } from "../utils/BaseTypes";
4
+
5
+ export type TableType = {
6
+ children: ReactNode,
7
+ size?: string,
8
+ className?: string,
9
+ } & BaseTableType
@@ -0,0 +1,3 @@
1
+ import Table from "./Table"
2
+ export default Table
3
+ export type { TableType } from "./Table.types"
@@ -0,0 +1,233 @@
1
+ import React, { createContext, forwardRef, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"
2
+
3
+ import { TabsButtonType, TabsContentType, TabsContextType, TabsControlsType, TabsPageType, TabsType, TabsScrollButtonType } from "./Tabs.types"
4
+
5
+ import mergeClassnames from "../utils/MergeClassnames"
6
+ import mergeRefs from "../utils/MergeRefs"
7
+ import { useScreenSize, useQueryParams } from "../hooks"
8
+
9
+ const TabsContext = createContext<TabsContextType | null>(null)
10
+ const TabsContextProvider = ({children, value}:{children: React.ReactNode, value: TabsContextType}) => {
11
+ return (
12
+ <TabsContext.Provider value={value}>
13
+ {children}
14
+ </TabsContext.Provider>
15
+ )
16
+ }
17
+ const useTabsContext = () => {
18
+ const context = useContext(TabsContext)
19
+ if(!context) {
20
+ throw new Error(
21
+ "useTabContext has to be used within a TabContextProvider!"
22
+ )
23
+ }
24
+ return context
25
+ }
26
+
27
+ const Tabs = forwardRef<HTMLDivElement, TabsType>(({children, className, controlId, activeClassName, defaultActive, active, onTabChange, queryParam, setQueryParam, ...restProps}, ref) => {
28
+ const tabIdsMap = new Map()
29
+ Array.from(document.querySelectorAll<HTMLElement>('[data-tabd]')).forEach(el => tabIdsMap.set(el.dataset.tabid,1) )
30
+
31
+ const isTabId: boolean = tabIdsMap.get(queryParam) && queryParam
32
+ const [activeTab, setActiveTab] = useState<string>(isTabId ? queryParam! : defaultActive)
33
+
34
+
35
+ const activeClass= activeClassName ?? "sg-active"
36
+ if(active && activeTab != active) {
37
+ setActiveTab(active)
38
+ }
39
+
40
+ if(active && !onTabChange || !active && onTabChange) {
41
+ throw new Error(
42
+ "If you control the tabs externally, you need both an 'active' state and a function 'onTabChange' that controls the 'active' state!"
43
+ )
44
+ }
45
+ if(queryParam && !setQueryParam || !queryParam && setQueryParam) {
46
+ throw new Error(
47
+ "If you control the query parameters externally, you need both an 'queryParam' state and a function 'setQueryParam' that controls the 'queryParam' state!"
48
+ )
49
+ }
50
+
51
+
52
+ const contextValue = useMemo(() => ({
53
+ activeTab,
54
+ setActiveTab,
55
+ controlId,
56
+ activeClass,
57
+ onTabChange,
58
+ setQueryParam
59
+ }), [activeTab, activeClass, controlId, activeClass, onTabChange])
60
+ return (
61
+ <TabsContextProvider value={contextValue}>
62
+ <div ref={ref} id={controlId+"-tab-wrapper"} className={`sg-tabs${className ? " "+className: ""}`} {...restProps}>
63
+ {children}
64
+ </div>
65
+ </TabsContextProvider>
66
+ )
67
+ })
68
+ Tabs.displayName = "Tabs"
69
+
70
+ const ScrollButton = forwardRef<HTMLButtonElement, TabsScrollButtonType>( ({className, left = true, controlsRef, style, ...restProps}, ref) => {
71
+ const internalRef = useRef<HTMLButtonElement>(null)
72
+
73
+ const handleScrollBy = () => {
74
+ if(!controlsRef || !controlsRef.current) return
75
+
76
+ const tabControls = controlsRef.current
77
+ const toScroll = (left ? -1 : 1) * 32
78
+ tabControls.scrollBy(toScroll, 0)
79
+ }
80
+
81
+ let scrollIntervalId: NodeJS.Timeout
82
+ const handlePointerDown = (event: React.MouseEvent<HTMLButtonElement>) => {
83
+ clearInterval(scrollIntervalId)
84
+ scrollIntervalId = setInterval(handleScrollBy, 20)
85
+ }
86
+ const handlePointerUp = (event: React.MouseEvent<HTMLButtonElement>) => {
87
+ clearInterval(scrollIntervalId)
88
+ }
89
+ const handleKeyDown = (event: React.KeyboardEvent<HTMLButtonElement> ) => {
90
+ if(event.key === "Space" || event.key === "Enter") {
91
+ clearInterval(scrollIntervalId)
92
+ scrollIntervalId = setInterval(handleScrollBy, 20)
93
+ return
94
+ }
95
+ clearInterval(scrollIntervalId)
96
+ }
97
+ const handleKeyUp = (event: React.KeyboardEvent<HTMLButtonElement> ) => {
98
+ clearInterval(scrollIntervalId)
99
+ }
100
+
101
+
102
+
103
+ return (
104
+ <button
105
+ type="button" ref={mergeRefs([ref, internalRef])} data-position={left ? "left":"right"}
106
+ onPointerDown={handlePointerDown} onPointerUp={handlePointerUp} onKeyDown={handleKeyDown} onKeyUp={handleKeyUp}
107
+ className={mergeClassnames("sg-tabs-button", "sg-tabs-scroll-button", className)}
108
+ {...restProps} >
109
+ <span className="visually-hidden">Scroll tab controls {left ? "left":"right"}</span>
110
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256">
111
+ <polyline
112
+ points={left ? "144 88 104 128 144 168" : "112 88 152 128 112 168" } //168 64 104 128 168 192
113
+ fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="32"
114
+ />
115
+ </svg>
116
+ </button>
117
+ )
118
+ })
119
+
120
+ const Controls = forwardRef<HTMLDivElement, TabsControlsType>( ({children, className, ...restProps}, ref) => {
121
+ const { controlId, activeClass } = useTabsContext()
122
+ const [ isOverflow, setIsOverflow ] = useState(false)
123
+ const internalRef = useRef<HTMLDivElement>(null)
124
+
125
+ const checkWidthOverflow = () => {
126
+ if(!internalRef.current) return
127
+
128
+ const el = internalRef.current
129
+ setIsOverflow(el.clientWidth < el.scrollWidth)
130
+ }
131
+ useLayoutEffect(() => {
132
+ //console.log(internalRef.current)
133
+ checkWidthOverflow()
134
+ }, [children])
135
+ useEffect(() => {
136
+ window.addEventListener("resize", checkWidthOverflow, true)
137
+ return function cleanup() {
138
+ window.removeEventListener("resize", checkWidthOverflow, true)
139
+ }
140
+ }, [])
141
+
142
+ const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
143
+ const key = event.key
144
+ const tabsControls = internalRef.current
145
+ if(!tabsControls) return
146
+
147
+ const tabControlsChildren = Array.from(tabsControls.children) as HTMLElement[]
148
+ let newIndex
149
+ if(key === "ArrowRight" || key === "ArrowLeft") {
150
+ event.preventDefault()
151
+ const activeTab = document.querySelector(".sg-tabs-button"+"."+activeClass) as HTMLElement
152
+ const activeTabIndex = tabControlsChildren.indexOf(activeTab)
153
+ const indexChange = key === "ArrowRight" ? 1 : -1
154
+ const isChangeTooBig = activeTabIndex + indexChange >= tabControlsChildren.length
155
+ const isChangeNegative = activeTabIndex + indexChange < 0
156
+ newIndex = isChangeNegative ? tabControlsChildren.length - 1 : (isChangeTooBig ? 0 : activeTabIndex + indexChange)
157
+ } else if (key === "Home" || key === "End") {
158
+ event.preventDefault()
159
+ newIndex = key === "Home" ? 0 : tabControlsChildren.length -1
160
+ }
161
+ if(newIndex === undefined || newIndex === null) return
162
+ tabControlsChildren[newIndex].focus()
163
+ tabControlsChildren[newIndex].click()
164
+ }
165
+ //div style={{maxWidth:"100%", display:"flex", overflowX:"auto"}}
166
+ return (
167
+ <div tabIndex={-1} onKeyDown={(event) => handleKeyDown(event)} role="tablist" id={controlId+"-tab-controls"} ref={mergeRefs([ref, internalRef])} className={`sg-tabs-controls${className ? " "+className:""}`} {...restProps}>
168
+ {isOverflow ? <ScrollButton controlsRef={internalRef} /> : null }
169
+ {children}
170
+ {isOverflow ? <ScrollButton controlsRef={internalRef} left={false}/> : null }
171
+ </div>
172
+ )
173
+ })
174
+ Controls.displayName = "TabsControl"
175
+
176
+ const Button = forwardRef<HTMLButtonElement, TabsButtonType>( ({children, className, onClick, tabId, id,...restProps}, ref) => {
177
+ const { activeTab, setActiveTab, setQueryParam, activeClass, onTabChange } = useTabsContext()
178
+ const isActiveTab = activeTab === tabId
179
+ const classNameComputed = "sg-tabs-button" + (className ? " "+className:"") + (isActiveTab ? " "+activeClass : "")
180
+ const handleClick = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
181
+ if(onTabChange) {
182
+ onTabChange(tabId)
183
+ }
184
+ else {
185
+ setActiveTab(tabId)
186
+ }
187
+ if(onClick) {
188
+ onClick(event)
189
+ }
190
+ if(setQueryParam) {
191
+ setQueryParam(tabId)
192
+ }
193
+ }
194
+ return (
195
+ <button
196
+ role="tab" type="button" id={tabId+"-button"} ref={ref} onClick={event => handleClick(event)} className={classNameComputed} {...restProps}
197
+ tabIndex={isActiveTab ? 0:-1} aria-selected={isActiveTab ? "true":"false"} aria-controls={tabId+"-page"}
198
+ >
199
+ {children}
200
+ </button>
201
+ )
202
+ })
203
+ Button.displayName = "TabsButton"
204
+
205
+ const Content = forwardRef<HTMLDivElement, TabsContentType>( ({children, className, ...restProps}, ref) => {
206
+ return (
207
+ <div role="none" ref={ref} className={`sg-tabs-content${className ? " "+className:""}`} {...restProps}>
208
+ {children}
209
+ </div>
210
+ )
211
+ })
212
+ Content.displayName = "TabsContent"
213
+
214
+ const Page = forwardRef<HTMLDivElement, TabsPageType>( ({children, className, tabId, ...restProps}, ref) => {
215
+ const { activeTab, activeClass } = useTabsContext()
216
+ const classNameComputed = "sg-tabs-page" + (className ? " "+className:"") + (activeTab === tabId ? " "+activeClass : "")
217
+ return (
218
+ <div
219
+ role="tabpanel" id={tabId+"-page"} data-tabid={tabId} aria-labelledby={tabId+"-button"}
220
+ ref={ref} className={classNameComputed} {...restProps}
221
+ >
222
+ {children}
223
+ </div>
224
+ )
225
+ })
226
+ Page.displayName = "TabsPage"
227
+
228
+ export default Object.assign(Tabs, {
229
+ Controls: Controls,
230
+ Button: Button,
231
+ Content: Content,
232
+ Page: Page,
233
+ })
@@ -0,0 +1,52 @@
1
+ import { BaseButtonType, BaseDivType } from "../utils/BaseTypes"
2
+
3
+ export type TabsContextType = {
4
+ activeTab: string,
5
+ setActiveTab: React.Dispatch<React.SetStateAction<string>>,
6
+ controlId: string,
7
+ activeClass: string,
8
+ onTabChange: Function | undefined,
9
+ setQueryParam?: Function
10
+ }
11
+
12
+ export type TabsType = {
13
+ children: React.ReactNode,
14
+ className?: string,
15
+ controlId: string,
16
+ activeClassName?: string,
17
+ defaultActive: string,
18
+ active?: string,
19
+ onTabChange?: Function,
20
+ queryParam?: string,
21
+ setQueryParam?: Function
22
+ } & BaseDivType
23
+
24
+ export type TabsControlsType = {
25
+ children: React.ReactNode,
26
+ className?: string,
27
+ } & BaseDivType
28
+
29
+ export type TabsScrollButtonType = {
30
+ className?: string,
31
+ left?: boolean,
32
+ controlsRef?: React.RefObject<HTMLDivElement>
33
+ } & BaseButtonType
34
+
35
+ export type TabsButtonType = {
36
+ children: React.ReactNode,
37
+ className?: string,
38
+ onClick?: Function,
39
+ tabId: string,
40
+ id?: string,
41
+ } & BaseButtonType
42
+
43
+ export type TabsContentType = {
44
+ children: React.ReactNode,
45
+ className?: string,
46
+ } & BaseDivType
47
+
48
+ export type TabsPageType = {
49
+ children: React.ReactNode,
50
+ className?: string,
51
+ tabId: string,
52
+ } & BaseDivType
@@ -0,0 +1,3 @@
1
+ import Tabs from "./Tabs"
2
+ export default Tabs
3
+ export type {TabsButtonType, TabsContentType, TabsContextType, TabsControlsType, TabsPageType, TabsType} from "./Tabs.types"
@@ -0,0 +1,21 @@
1
+ import React, { useState, forwardRef } from "react"
2
+
3
+ import { ToggleButtonType } from "./ToggleButton.types"
4
+
5
+ const ToggleButton = forwardRef<HTMLButtonElement, ToggleButtonType>( ({children, toggled="false", onClick, ...restProps}, ref) => {
6
+ const [ toggledInternal, setToggledInternal ] = useState<boolean>(toggled === "true" ? true : false)
7
+ const handleClick = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
8
+ setToggledInternal(prev => !prev)
9
+ if(onClick) {
10
+ onClick(event)
11
+ }
12
+ }
13
+ return (
14
+ <button onClick={(event) => handleClick(event)} data-toggled={toggledInternal} {...restProps}>
15
+ {children}
16
+ </button>
17
+ )
18
+ })
19
+ ToggleButton.displayName = "ToggleButton"
20
+
21
+ export default ToggleButton
@@ -0,0 +1,8 @@
1
+ import { ReactNode } from "react"
2
+ import { BaseButtonType } from "../utils/BaseTypes"
3
+
4
+ export type ToggleButtonType = {
5
+ children: ReactNode,
6
+ toggled?: boolean,
7
+ onClick?: <T>(event: T) => T
8
+ } & BaseButtonType
@@ -0,0 +1,3 @@
1
+ import ToggleButton from "./ToggleButton"
2
+ export default ToggleButton
3
+ export type {ToggleButtonType} from "./ToggleButton.types"
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 10 20">
2
+ <polygon points="1,1 1,19, 9,10" fill="#{$sg-arrow-fill}" stroke-width="2" stroke="#{$sg-arrow-fill}" />
3
+ <polyline points="1,1 9,10 1,19" fill="none" stroke="#{$sg-arrow-border}" /> </svg>
@@ -0,0 +1,39 @@
1
+ <svg viewBox="0 0 256 256" width="256" height="256" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
2
+ <rect x="0" y="0" width="100%" height="100%" fill="transparent" />
3
+ <line
4
+ x1="128" x2="128" y1="100" y2="165"
5
+ stroke="transparent" fill="transparent" stroke-width="20" stroke-linecap="round" stroke-linejoin="round"
6
+ />
7
+ <circle cx="128" cy="200" r="10" fill="transparent"/>
8
+ <path
9
+ d="
10
+ M32 240
11
+ L224 240
12
+ C244 240, 240 224, 240 224
13
+ L138 20
14
+ C133 10, 123 10, 118 20
15
+ L16 224
16
+ C16 224, 12 240, 32 240
17
+ Z
18
+ "
19
+ fill="transparent" stroke="transparent" stroke-width="20" stroke-linecap="round" stroke-linejoin="round"/>
20
+
21
+ <line
22
+ x1="128" x2="128" y1="83" y2="148"
23
+ stroke="white" fill="transparent" stroke-width="20" stroke-linecap="round" stroke-linejoin="round"
24
+ />
25
+ <circle cx="128" cy="183" r="10" fill="white"/>
26
+ <path
27
+ d="
28
+ M40 223
29
+ L216 223
30
+ C216 223, 232 191, 232 191
31
+ L153 33
32
+ C153 33, 103 33, 103 33
33
+ L24 191
34
+ C24 191, 40 223, 40 223
35
+ Z
36
+ "
37
+ fill="transparent" stroke="white" stroke-width="20" stroke-linecap="round" stroke-linejoin="round"/>
38
+
39
+ </svg>
@@ -1,6 +1,7 @@
1
- import useClassname from "./useClassname";
2
- import useDraggable from "./useDraggable";
3
- import useScreenSize from "./useScreenSize";
4
- import useKeepElementFocused from "./useKeepElementFocused";
5
- import useQueryParams from "./useQueryParams";
6
- export { useClassname, useDraggable, useScreenSize, useKeepElementFocused, useQueryParams };
1
+ import useClassname from "./useClassname";
2
+ import useDraggable from "./useDraggable";
3
+ import useScreenSize from "./useScreenSize";
4
+ import useKeepElementFocused from "./useKeepElementFocused";
5
+ import useQueryParams from "./useQueryParams";
6
+
7
+ export {useClassname, useDraggable, useScreenSize, useKeepElementFocused, useQueryParams}
@@ -0,0 +1,5 @@
1
+ const useClassname = (...args: (string | undefined)[]) => {
2
+ return args.filter(arg => arg != undefined).join(" ")
3
+ }
4
+
5
+ export default useClassname
@@ -0,0 +1,186 @@
1
+ import { useRef, useState, useEffect, RefObject, useLayoutEffect, cloneElement, ReactNode } from "react"
2
+ import { createPortal } from "react-dom"
3
+
4
+ type Coordinates = {
5
+ top?: number,
6
+ bottom?: number,
7
+ right?: number,
8
+ left?: number,
9
+ offSetTop:number,
10
+ offSetLeft:number,
11
+ width?: number | null
12
+ }
13
+ type Dimensions = {
14
+ height?: number,
15
+ width?: number
16
+ }
17
+ type Options = {
18
+ moveClass?: string,
19
+ draggable?: boolean
20
+ }
21
+
22
+ export const insertPhantomElement = (targetElement: HTMLElement, movingElement: HTMLElement, options: {after?:boolean, first?:boolean} = {after: true, first: true}) => {
23
+ const nextSibling = targetElement.nextElementSibling as HTMLElement
24
+ const previousSibling = targetElement.previousElementSibling as HTMLElement
25
+ const phantomData = document.querySelectorAll<HTMLElement>("[data-phantom='before'], [data-phantom='after']")
26
+ phantomData.forEach(element => {
27
+ if( targetElement !== element) {
28
+ element.dataset.phantom = "none"
29
+ //console.log("equal")
30
+ }
31
+ })
32
+ targetElement.dataset.phantom = options.after ? "after":"before"
33
+ if(options.after && nextSibling) { nextSibling.dataset.phantom = "before" }
34
+ if(!options.after && previousSibling) { previousSibling.dataset.phantom = "after" }
35
+
36
+ const { width, height } = movingElement.getBoundingClientRect()
37
+ const parentElement = targetElement.parentElement!
38
+
39
+ document.querySelectorAll(".phantom").forEach(phantom => phantom.remove())
40
+
41
+ const phantomDiv = document.createElement("li")
42
+ phantomDiv.classList.add("phantom")
43
+ phantomDiv.classList.add("sg-sublist")
44
+ phantomDiv.style.width = options.first ? width.toString()+"px" : ""
45
+ phantomDiv.style.height = height.toString()+"px"
46
+ parentElement.insertBefore(phantomDiv, options.after ? targetElement.nextSibling : targetElement)
47
+ }
48
+
49
+ const insertDraggedElement = () => {
50
+
51
+ }
52
+
53
+ /*
54
+ const useDraggable = (movingElementRef: RefObject<HTMLElement>, options: Options = {}) => {
55
+ const [ dragging, setDragging ] = useState<boolean>(false)
56
+
57
+ const onDragStart = (event: React.DragEvent) => {
58
+ console.log("drag start")
59
+ insertPhantomElement(movingElementRef.current!)
60
+ }
61
+
62
+ const onDragEnd = (event: React.DragEvent) => {
63
+ document.querySelectorAll(".phantom").forEach(phantom => phantom.remove())
64
+ }
65
+
66
+ return { onDragStart, onDragEnd }
67
+ }
68
+ */
69
+
70
+ const useDraggable = (movingElementRef: RefObject<HTMLElement>, options: Options = {draggable: false}) => {
71
+ const [coordinates, setCoordinates] = useState<Coordinates>({offSetTop:0, offSetLeft:0})
72
+ const [ isMouseDown, setIsMouseDown ] = useState<boolean>(false)
73
+
74
+ const elementWithPhantom = useRef<HTMLElement | null>(null)
75
+ const isMouseDownRef = useRef<boolean>(isMouseDown)
76
+ const coordinatesRef = useRef<Coordinates>(coordinates)
77
+ useEffect(() => {
78
+ isMouseDownRef.current = isMouseDown
79
+ }, [isMouseDown])
80
+ useEffect(() => {
81
+ coordinatesRef.current = coordinates
82
+ }, [coordinates])
83
+
84
+ useEffect(() => {
85
+ if(movingElementRef && options.draggable) {
86
+ const movingElement = movingElementRef.current!
87
+
88
+ const onPointerDown = (event: PointerEvent) => {
89
+ event.stopPropagation()
90
+ const { width } = (event.target as HTMLElement).getBoundingClientRect()
91
+
92
+ insertPhantomElement(movingElement, movingElement, {first: true})
93
+
94
+ const { top, left} = movingElement.getBoundingClientRect()
95
+ //Initial coordinates & setup for moving
96
+ setCoordinates(prev => ({
97
+ ...prev,
98
+ top: top,
99
+ left: left,
100
+ offSetTop:event.clientY - top,
101
+ offSetLeft:event.clientX - left,
102
+ width: width
103
+ }))
104
+ movingElement.style.setProperty("user-select", "none")
105
+ movingElement.setPointerCapture(event.pointerId)
106
+ movingElement.classList.add("moving")
107
+ setIsMouseDown(true)
108
+ }
109
+ const onPointerUp = (event: PointerEvent) => {
110
+ if(isMouseDownRef.current) {
111
+ const phantomElement = document.querySelectorAll(".phantom")[0]
112
+ const phantomElementParent = phantomElement.parentElement!
113
+
114
+ phantomElementParent.insertBefore(movingElement, phantomElement)
115
+
116
+ document.querySelectorAll(".phantom").forEach(phantom => phantom.remove())
117
+
118
+ movingElement.style.removeProperty("user-select")
119
+ movingElement.classList.remove("moving")
120
+ setCoordinates(prev => ({
121
+ ...prev,
122
+ width: null
123
+ }))
124
+ setIsMouseDown(false)
125
+ }
126
+ }
127
+ const onPointerMove = (event: PointerEvent) => {
128
+ if(isMouseDownRef.current) {
129
+ const isTopOver = event.clientY - coordinatesRef.current.offSetTop < 0
130
+ const isBottomOver = window.innerHeight - (event.clientY - coordinatesRef.current.offSetTop + movingElement.offsetHeight) < 0
131
+ const isLeftOver = event.clientX - coordinatesRef.current.offSetLeft < 0
132
+ const isRightOver = window.innerWidth - (event.clientX - coordinatesRef.current.offSetLeft + movingElement.offsetWidth) < 0
133
+
134
+ let topCoordinate = isTopOver ? 0 : event.clientY - coordinatesRef.current.offSetTop
135
+ topCoordinate = isBottomOver ? window.innerHeight - movingElement.offsetHeight : topCoordinate
136
+
137
+ let leftCoordinate = isLeftOver ? 0 : event.clientX - coordinatesRef.current.offSetLeft
138
+ leftCoordinate = isRightOver ? window.innerWidth - movingElement.offsetWidth : leftCoordinate
139
+
140
+ setCoordinates(prev => ({
141
+ ...prev,
142
+ top:topCoordinate,
143
+ left:leftCoordinate
144
+ }))
145
+
146
+ movingElement.hidden = true
147
+ const elemBelow = document.elementFromPoint(event.clientX, event.clientY)
148
+ movingElement.hidden = false
149
+ const isElemeBelowDropable = elemBelow?.classList.contains("sg-list-item") || elemBelow?.classList.contains("sg-sublist")
150
+ const elemBelowClosestDroppable = isElemeBelowDropable ? elemBelow as HTMLElement : elemBelow?.closest(".sg-list-item, .sg-sublist") as HTMLElement
151
+ console.log(elemBelow, elemBelowClosestDroppable)
152
+
153
+ if(!elemBelowClosestDroppable || elemBelow?.classList.contains("phantom")) { return }
154
+
155
+ if(elementWithPhantom.current != elemBelowClosestDroppable) {
156
+ elementWithPhantom.current = elemBelowClosestDroppable
157
+ }
158
+
159
+ const {top, left, width, height} = elementWithPhantom.current.getBoundingClientRect()
160
+ const phantomPosition = elementWithPhantom.current.dataset.phantom
161
+
162
+ const afterTest = event.clientY - top >= height/2 && phantomPosition != "after"
163
+ const beforeTest = event.clientY - top < height/2 && phantomPosition != "before"
164
+ if(afterTest || beforeTest) {
165
+ insertPhantomElement(elementWithPhantom.current, movingElement, {after: afterTest ? true : false})
166
+ }
167
+ }
168
+ }
169
+
170
+ const bubble = false
171
+
172
+ movingElement.addEventListener("pointerdown", onPointerDown, bubble)
173
+ movingElement.addEventListener("pointerup", onPointerUp, bubble)
174
+ movingElement.addEventListener("pointermove", onPointerMove, bubble)
175
+ return function cleanup() {
176
+ movingElement.removeEventListener("pointerdown", onPointerDown, bubble)
177
+ movingElement.removeEventListener("pointerup", onPointerUp, bubble)
178
+ movingElement.removeEventListener("pointermove", onPointerMove, bubble)
179
+ }
180
+ }
181
+ },[])
182
+
183
+ return { coordinates, isMouseDown}
184
+ }
185
+
186
+ export default useDraggable
@@ -0,0 +1,37 @@
1
+ import { useEffect } from "react";
2
+
3
+ const useKeepElementFocused = (elementRef:any) => {
4
+ useEffect(() => {
5
+ const onKeyDown = (event: React.KeyboardEvent | KeyboardEvent) => {
6
+ const focusableElements = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
7
+ const modal = elementRef.current as HTMLElement
8
+ if(modal) {
9
+ const firstFocusableElement = modal.querySelectorAll<HTMLElement>(focusableElements)[0]
10
+ const focusableContent = modal.querySelectorAll<HTMLElement>(focusableElements)
11
+ const lastFocusableElement = focusableContent[focusableContent.length - 1]
12
+ let isTabPressed = event.key === 'Tab' || event.keyCode === 9;
13
+
14
+ if (!isTabPressed) {
15
+ return;
16
+ }
17
+
18
+ if (event.shiftKey) {
19
+ if (document.activeElement === firstFocusableElement) {
20
+ lastFocusableElement.focus()
21
+ event.preventDefault()
22
+ }
23
+ } else if (document.activeElement === lastFocusableElement) {
24
+ firstFocusableElement.focus();
25
+ event.preventDefault()
26
+ }
27
+ }
28
+ }
29
+ document.addEventListener('keydown', onKeyDown, true )
30
+
31
+ return function cleanup() {
32
+ document.removeEventListener('keydown', onKeyDown, true )
33
+ }
34
+ }, [elementRef])
35
+ }
36
+
37
+ export default useKeepElementFocused
@@ -0,0 +1,12 @@
1
+
2
+ const useQueryParams = (queryParam: string) => {
3
+ const url = new URL(window.location.href);
4
+ const param = url.searchParams.get(queryParam)// ? url.searchParams.get(queryParam) : url; // gremlin
5
+ const setQuery = (query: string, newParam:string) => {
6
+ url.searchParams.set(query, newParam)
7
+ history.replaceState(history.state, '', url);
8
+ }
9
+ return [param, setQuery]
10
+ }
11
+ // localhost:3000?name="gremlin"
12
+ export default useQueryParams