stargazer-ui 1.2.0 → 1.2.1

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 (349) 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 +49 -0
  7. package/dev/index.scss +59 -0
  8. package/dev/pages/ButtonPage.jsx +44 -0
  9. package/dev/pages/CardPage.jsx +81 -0
  10. package/dev/pages/DropdownPage.jsx +31 -0
  11. package/dev/pages/FormPage.jsx +130 -0
  12. package/dev/pages/ListPage.jsx +52 -0
  13. package/dev/pages/ModalPage.jsx +37 -0
  14. package/dev/pages/OverlayPage.jsx +35 -0
  15. package/dev/pages/components.jsx +19 -0
  16. package/{styles → dev}/stargazerui.css +392 -3075
  17. package/dev/stargazerui.css.map +1 -0
  18. package/dev/test.jsx +88 -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 +17 -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 +358 -0
  38. package/src/Dropdown/Dropdown.types.ts +52 -0
  39. package/src/Dropdown/index.ts +4 -0
  40. package/src/FileUploadButton/FileUploadButton.tsx +27 -0
  41. package/src/FileUploadButton/FileUploadButton.types.ts +9 -0
  42. package/src/FileUploadButton/index.ts +3 -0
  43. package/src/FloatingLabel/FloatingLabel.tsx +22 -0
  44. package/src/FloatingLabel/FloatingLabel.types.ts +11 -0
  45. package/src/FloatingLabel/index.ts +3 -0
  46. package/src/Form/Form.tsx +445 -0
  47. package/src/Form/Form.types.ts +117 -0
  48. package/src/Form/index.ts +4 -0
  49. package/src/InputGroup/InputGroup.tsx +46 -0
  50. package/src/InputGroup/InputGroup.types.ts +21 -0
  51. package/src/InputGroup/index.ts +4 -0
  52. package/src/List/List.tsx +112 -0
  53. package/src/List/List.types.ts +34 -0
  54. package/src/List/index.ts +4 -0
  55. package/src/Modal/Modal.tsx +220 -0
  56. package/src/Modal/Modal.types.ts +49 -0
  57. package/src/Modal/index.ts +4 -0
  58. package/src/Nav/Nav.tsx +43 -0
  59. package/src/Nav/Nav.types.ts +21 -0
  60. package/src/Nav/index.ts +4 -0
  61. package/src/NavBar/Navbar.tsx +57 -0
  62. package/src/NavBar/Navbar.types.ts +24 -0
  63. package/src/NavBar/index.ts +4 -0
  64. package/src/NavDropdown/NavDropdown.tsx +93 -0
  65. package/src/NavDropdown/NavDropdown.types.ts +6 -0
  66. package/src/NavDropdown/index.ts +3 -0
  67. package/src/Overlay/Overlay.tsx +277 -0
  68. package/src/Overlay/Overlay.types.ts +20 -0
  69. package/{Overlay/index.d.ts → src/Overlay/index.ts} +3 -3
  70. package/src/Popout/Popout.tsx +155 -0
  71. package/src/Popout/Popout.types.ts +42 -0
  72. package/src/Popout/index.ts +3 -0
  73. package/src/Spinner/Spinner.tsx +15 -0
  74. package/src/Spinner/Spinner.types.ts +7 -0
  75. package/src/Spinner/index.ts +3 -0
  76. package/src/Table/Table.tsx +16 -0
  77. package/src/Table/Table.types.ts +9 -0
  78. package/src/Table/index.ts +3 -0
  79. package/src/Tabs/Tabs.tsx +215 -0
  80. package/src/Tabs/Tabs.types.ts +49 -0
  81. package/src/Tabs/index.ts +3 -0
  82. package/src/ToggleButton/ToggleButton.tsx +21 -0
  83. package/src/ToggleButton/ToggleButton.types.ts +8 -0
  84. package/src/ToggleButton/index.ts +3 -0
  85. package/{hooks/index.d.ts → src/hooks/index.ts} +6 -5
  86. package/src/hooks/useClassname.ts +5 -0
  87. package/src/hooks/useDraggable.ts +186 -0
  88. package/src/hooks/useKeepElementFocused.ts +37 -0
  89. package/src/hooks/useScreenSize.ts +24 -0
  90. package/src/index.ts +21 -0
  91. package/src/styles/_Card.scss +166 -0
  92. package/src/styles/_CloseButton.scss +51 -0
  93. package/src/styles/_CustomButton.scss +132 -0
  94. package/src/styles/_Dropdown.scss +120 -0
  95. package/src/styles/_FloatingLabel.scss +56 -0
  96. package/src/styles/_Forms.scss +7 -0
  97. package/src/styles/_Grid.scss +178 -0
  98. package/src/styles/_InputGroup.scss +71 -0
  99. package/src/styles/_List.scss +62 -0
  100. package/src/styles/_Modal.scss +234 -0
  101. package/src/styles/_ModalOld.scss +242 -0
  102. package/src/styles/_Nav.scss +36 -0
  103. package/src/styles/_NavBar.scss +116 -0
  104. package/src/styles/_NavDropdown.scss +34 -0
  105. package/src/styles/_OffCanvas.scss +260 -0
  106. package/src/styles/_OverLay.scss +56 -0
  107. package/src/styles/_Popout.scss +75 -0
  108. package/src/styles/_Spinner.scss +19 -0
  109. package/src/styles/_Table.scss +34 -0
  110. package/src/styles/_Tabs.scss +129 -0
  111. package/src/styles/_colors.scss +510 -0
  112. package/src/styles/_components.scss +40 -0
  113. package/src/styles/_functions.scss +134 -0
  114. package/src/styles/_mixins.scss +26 -0
  115. package/src/styles/_reset.scss +231 -0
  116. package/src/styles/_utilities.scss +2480 -0
  117. package/src/styles/_variables.scss +146 -0
  118. package/src/styles/forms/_FormCheck.scss +269 -0
  119. package/src/styles/forms/_FormControl.scss +102 -0
  120. package/src/styles/forms/_FormGroup.scss +21 -0
  121. package/src/styles/forms/_FormLabel.scss +3 -0
  122. package/src/styles/forms/_FormSelect.scss +164 -0
  123. package/src/styles/forms/_FormSlider.scss +116 -0
  124. package/src/styles/forms/_FormText.scss +6 -0
  125. package/{utils/BaseTypes.d.ts → src/utils/BaseTypes.ts} +30 -23
  126. package/src/utils/ContrastingColor.ts +25 -0
  127. package/src/utils/FileImportExport.js +50 -0
  128. package/src/utils/MergeClassnames.ts +5 -0
  129. package/src/utils/MergeRefs.ts +12 -0
  130. package/src/vite-env.d.ts +1 -0
  131. package/tsconfig-build.json +4 -0
  132. package/tsconfig.json +79 -0
  133. package/tsconfig.node.json +10 -0
  134. package/types/BaseTypes.d.ts +19 -0
  135. package/{Button → types/components/Button}/Button.types.d.ts +2 -1
  136. package/types/components/Button/index.d.ts +1 -0
  137. package/{Card → types/components/Card}/Card.types.d.ts +1 -3
  138. package/types/components/Card/index.d.ts +1 -0
  139. package/{CloseButton → types/components/CloseButton}/CloseButton.types.d.ts +1 -1
  140. package/types/components/CloseButton/index.d.ts +1 -0
  141. package/{Dropdown → types/components/Dropdown}/Dropdown.types.d.ts +10 -3
  142. package/types/components/Dropdown/index.d.ts +1 -0
  143. package/{FloatingLabel → types/components/FloatingLabel}/FloatingLabel.types.d.ts +1 -1
  144. package/types/components/FloatingLabel/index.d.ts +1 -0
  145. package/types/components/Form/Form.d.ts +17 -0
  146. package/types/components/Form/Form.types.d.ts +50 -0
  147. package/types/components/Form/index.d.ts +1 -0
  148. package/types/components/InputGroup/InputGroup.d.ts +6 -0
  149. package/types/components/InputGroup/InputGroup.types.d.ts +10 -0
  150. package/types/components/InputGroup/index.d.ts +1 -0
  151. package/{Modal → types/components/Modal}/Modal.d.ts +1 -1
  152. package/{Modal → types/components/Modal}/Modal.types.d.ts +2 -3
  153. package/types/components/Modal/index.d.ts +1 -0
  154. package/{Nav → types/components/Nav}/Nav.types.d.ts +1 -1
  155. package/types/components/Nav/index.d.ts +1 -0
  156. package/{NavBar → types/components/NavBar}/Navbar.d.ts +2 -1
  157. package/{NavBar → types/components/NavBar}/Navbar.types.d.ts +1 -2
  158. package/types/components/NavBar/index.d.ts +1 -0
  159. package/{NavDropdown → types/components/NavDropdown}/NavDropdown.d.ts +2 -2
  160. package/types/components/NavDropdown/index.d.ts +1 -0
  161. package/{Popout → types/components/Popout}/Popout.types.d.ts +1 -1
  162. package/types/components/Popout/index.d.ts +1 -0
  163. package/types/components/Spinner/index.d.ts +1 -0
  164. package/{Table → types/components/Table}/Table.types.d.ts +1 -1
  165. package/types/components/Table/index.d.ts +1 -0
  166. package/{Tabs → types/components/Tabs}/Tabs.types.d.ts +1 -9
  167. package/types/components/Tabs/index.d.ts +1 -0
  168. package/types/components/ToggleButton/ToggleButton.d.ts +9 -0
  169. package/types/components/ToggleButton/ToggleButton.types.d.ts +0 -0
  170. package/types/components/ToggleButton/index.d.ts +1 -0
  171. package/types/components/index.d.ts +16 -0
  172. package/types/index.d.ts +1 -0
  173. package/vite.config.js +57 -0
  174. package/vite.config.js.timestamp-1708777378490-e94428ceb2bf9.mjs +42 -0
  175. package/Bar/Bar.type.d.ts +0 -6
  176. package/Bar/index.js +0 -2
  177. package/Bar/index.js.map +0 -1
  178. package/Bar/package.json +0 -1
  179. package/Button/Button.js +0 -12
  180. package/Button/Button.js.map +0 -1
  181. package/Button/index.d.ts +0 -3
  182. package/Button/index.js +0 -7
  183. package/Button/index.js.map +0 -1
  184. package/Button/package.json +0 -1
  185. package/ButtonGroup/ButtonGroup.d.ts +0 -4
  186. package/ButtonGroup/ButtonGroup.js +0 -10
  187. package/ButtonGroup/ButtonGroup.js.map +0 -1
  188. package/ButtonGroup/ButtonGroup.types.d.ts +0 -7
  189. package/ButtonGroup/index.d.ts +0 -3
  190. package/ButtonGroup/index.js +0 -7
  191. package/ButtonGroup/index.js.map +0 -1
  192. package/ButtonGroup/package.json +0 -1
  193. package/Card/Card.js +0 -41
  194. package/Card/Card.js.map +0 -1
  195. package/Card/index.d.ts +0 -3
  196. package/Card/index.js +0 -7
  197. package/Card/index.js.map +0 -1
  198. package/Card/package.json +0 -1
  199. package/CloseButton/CloseButton.js +0 -11
  200. package/CloseButton/CloseButton.js.map +0 -1
  201. package/CloseButton/index.d.ts +0 -3
  202. package/CloseButton/index.js +0 -7
  203. package/CloseButton/index.js.map +0 -1
  204. package/CloseButton/package.json +0 -1
  205. package/Dropdown/Dropdown.js +0 -292
  206. package/Dropdown/Dropdown.js.map +0 -1
  207. package/Dropdown/index.d.ts +0 -4
  208. package/Dropdown/index.js +0 -8
  209. package/Dropdown/index.js.map +0 -1
  210. package/Dropdown/package.json +0 -1
  211. package/FileUploadButton/FileUploadButton.d.ts +0 -4
  212. package/FileUploadButton/FileUploadButton.js +0 -21
  213. package/FileUploadButton/FileUploadButton.js.map +0 -1
  214. package/FileUploadButton/FileUploadButton.types.d.ts +0 -7
  215. package/FileUploadButton/index.d.ts +0 -3
  216. package/FileUploadButton/index.js +0 -7
  217. package/FileUploadButton/index.js.map +0 -1
  218. package/FileUploadButton/package.json +0 -1
  219. package/FloatingLabel/FloatingLabel.js +0 -17
  220. package/FloatingLabel/FloatingLabel.js.map +0 -1
  221. package/FloatingLabel/index.d.ts +0 -3
  222. package/FloatingLabel/index.js +0 -7
  223. package/FloatingLabel/index.js.map +0 -1
  224. package/FloatingLabel/package.json +0 -1
  225. package/Form/Form.d.ts +0 -38
  226. package/Form/Form.js +0 -308
  227. package/Form/Form.js.map +0 -1
  228. package/Form/Form.types.d.ts +0 -105
  229. package/Form/index.d.ts +0 -4
  230. package/Form/index.js +0 -8
  231. package/Form/index.js.map +0 -1
  232. package/Form/package.json +0 -1
  233. package/InputGroup/InputGroup.d.ts +0 -7
  234. package/InputGroup/InputGroup.js +0 -31
  235. package/InputGroup/InputGroup.js.map +0 -1
  236. package/InputGroup/InputGroup.types.d.ts +0 -16
  237. package/InputGroup/index.d.ts +0 -4
  238. package/InputGroup/index.js +0 -7
  239. package/InputGroup/index.js.map +0 -1
  240. package/InputGroup/package.json +0 -1
  241. package/List/List.d.ts +0 -14
  242. package/List/List.js +0 -76
  243. package/List/List.js.map +0 -1
  244. package/List/List.types.d.ts +0 -28
  245. package/List/index.d.ts +0 -3
  246. package/List/index.js +0 -7
  247. package/List/index.js.map +0 -1
  248. package/List/package.json +0 -1
  249. package/Modal/Modal.js +0 -160
  250. package/Modal/Modal.js.map +0 -1
  251. package/Modal/index.d.ts +0 -3
  252. package/Modal/index.js +0 -7
  253. package/Modal/index.js.map +0 -1
  254. package/Modal/package.json +0 -1
  255. package/Nav/Nav.js +0 -28
  256. package/Nav/Nav.js.map +0 -1
  257. package/Nav/index.d.ts +0 -4
  258. package/Nav/index.js +0 -7
  259. package/Nav/index.js.map +0 -1
  260. package/Nav/package.json +0 -1
  261. package/NavBar/Navbar.js +0 -36
  262. package/NavBar/Navbar.js.map +0 -1
  263. package/NavBar/index.d.ts +0 -4
  264. package/NavBar/index.js +0 -8
  265. package/NavBar/index.js.map +0 -1
  266. package/NavBar/package.json +0 -1
  267. package/NavDropdown/NavDropdown.js +0 -77
  268. package/NavDropdown/NavDropdown.js.map +0 -1
  269. package/NavDropdown/index.d.ts +0 -3
  270. package/NavDropdown/index.js +0 -7
  271. package/NavDropdown/index.js.map +0 -1
  272. package/NavDropdown/package.json +0 -1
  273. package/Overlay/Overlay.d.ts +0 -4
  274. package/Overlay/Overlay.js +0 -228
  275. package/Overlay/Overlay.js.map +0 -1
  276. package/Overlay/Overlay.types.d.ts +0 -18
  277. package/Overlay/index.js +0 -7
  278. package/Overlay/index.js.map +0 -1
  279. package/Overlay/package.json +0 -1
  280. package/Popout/Popout.js +0 -110
  281. package/Popout/Popout.js.map +0 -1
  282. package/Popout/index.d.ts +0 -3
  283. package/Popout/index.js +0 -7
  284. package/Popout/index.js.map +0 -1
  285. package/Popout/package.json +0 -1
  286. package/Spinner/Spinner.js +0 -12
  287. package/Spinner/Spinner.js.map +0 -1
  288. package/Spinner/index.d.ts +0 -3
  289. package/Spinner/index.js +0 -7
  290. package/Spinner/index.js.map +0 -1
  291. package/Spinner/package.json +0 -1
  292. package/Table/Table.js +0 -11
  293. package/Table/Table.js.map +0 -1
  294. package/Table/index.d.ts +0 -3
  295. package/Table/index.js +0 -7
  296. package/Table/index.js.map +0 -1
  297. package/Table/package.json +0 -1
  298. package/Tabs/Tabs.js +0 -158
  299. package/Tabs/Tabs.js.map +0 -1
  300. package/Tabs/index.d.ts +0 -3
  301. package/Tabs/index.js +0 -7
  302. package/Tabs/index.js.map +0 -1
  303. package/Tabs/package.json +0 -1
  304. package/ToggleButton/ToggleButton.d.ts +0 -4
  305. package/ToggleButton/ToggleButton.js +0 -17
  306. package/ToggleButton/ToggleButton.js.map +0 -1
  307. package/ToggleButton/ToggleButton.types.d.ts +0 -7
  308. package/ToggleButton/index.d.ts +0 -3
  309. package/ToggleButton/index.js +0 -7
  310. package/ToggleButton/index.js.map +0 -1
  311. package/ToggleButton/package.json +0 -1
  312. package/hooks/index.js +0 -6
  313. package/hooks/index.js.map +0 -1
  314. package/hooks/package.json +0 -1
  315. package/hooks/useClassname.d.ts +0 -2
  316. package/hooks/useClassname.js +0 -7
  317. package/hooks/useClassname.js.map +0 -1
  318. package/hooks/useDraggable.d.ts +0 -23
  319. package/hooks/useDraggable.js +0 -147
  320. package/hooks/useDraggable.js.map +0 -1
  321. package/hooks/useKeepElementFocused.d.ts +0 -2
  322. package/hooks/useKeepElementFocused.js +0 -37
  323. package/hooks/useKeepElementFocused.js.map +0 -1
  324. package/hooks/useScreenSize.d.ts +0 -5
  325. package/hooks/useScreenSize.js +0 -21
  326. package/hooks/useScreenSize.js.map +0 -1
  327. package/index.d.ts +0 -18
  328. package/index.js +0 -19
  329. package/index.js.map +0 -1
  330. package/styles/stargazerui.css.map +0 -1
  331. package/utils/ContrastingColor.d.ts +0 -1
  332. package/utils/MergeClassnames.d.ts +0 -2
  333. package/utils/MergeClassnames.js +0 -7
  334. package/utils/MergeClassnames.js.map +0 -1
  335. package/utils/MergeRefs.d.ts +0 -2
  336. package/utils/MergeRefs.js +0 -16
  337. package/utils/MergeRefs.js.map +0 -1
  338. /package/{Button → types/components/Button}/Button.d.ts +0 -0
  339. /package/{Card → types/components/Card}/Card.d.ts +0 -0
  340. /package/{CloseButton → types/components/CloseButton}/CloseButton.d.ts +0 -0
  341. /package/{Dropdown → types/components/Dropdown}/Dropdown.d.ts +0 -0
  342. /package/{FloatingLabel → types/components/FloatingLabel}/FloatingLabel.d.ts +0 -0
  343. /package/{Nav → types/components/Nav}/Nav.d.ts +0 -0
  344. /package/{NavDropdown → types/components/NavDropdown}/NavDropdown.types.d.ts +0 -0
  345. /package/{Popout → types/components/Popout}/Popout.d.ts +0 -0
  346. /package/{Spinner → types/components/Spinner}/Spinner.d.ts +0 -0
  347. /package/{Spinner → types/components/Spinner}/Spinner.types.d.ts +0 -0
  348. /package/{Table → types/components/Table}/Table.d.ts +0 -0
  349. /package/{Tabs → types/components/Tabs}/Tabs.d.ts +0 -0
@@ -0,0 +1,215 @@
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 } 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, ...restProps}, ref) => {
28
+ const [activeTab, setActiveTab] = useState<string>(defaultActive)
29
+ const activeClass= activeClassName ? activeClassName: "sg-active"
30
+ if(active && activeTab != active) {
31
+ setActiveTab(active)
32
+ }
33
+
34
+ if(active && !onTabChange || !active && onTabChange) {
35
+ throw new Error(
36
+ "If you control the tabs externally, you need both an 'active' state and a function 'onTabChange' that controls the 'active' state!"
37
+ )
38
+ }
39
+
40
+ const contextValue = useMemo(() => ({
41
+ activeTab,
42
+ setActiveTab,
43
+ controlId,
44
+ activeClass,
45
+ onTabChange
46
+ }), [activeTab, activeClass, controlId, activeClass, onTabChange])
47
+ return (
48
+ <TabsContextProvider value={contextValue}>
49
+ <div ref={ref} id={controlId+"-tab-wrapper"} className={`sg-tabs${className ? " "+className: ""}`} {...restProps}>
50
+ {children}
51
+ </div>
52
+ </TabsContextProvider>
53
+ )
54
+ })
55
+ Tabs.displayName = "Tabs"
56
+
57
+ const ScrollButton = forwardRef<HTMLButtonElement, TabsScrollButtonType>( ({className, left = true, controlsRef, style, ...restProps}, ref) => {
58
+ const internalRef = useRef<HTMLButtonElement>(null)
59
+
60
+ const handleScrollBy = () => {
61
+ if(!controlsRef || !controlsRef.current) return
62
+
63
+ const tabControls = controlsRef.current
64
+ const toScroll = (left ? -1 : 1) * 32
65
+ tabControls.scrollBy(toScroll, 0)
66
+ }
67
+
68
+ let scrollIntervalId: NodeJS.Timeout
69
+ const handlePointerDown = (event: React.MouseEvent<HTMLButtonElement>) => {
70
+ clearInterval(scrollIntervalId)
71
+ scrollIntervalId = setInterval(handleScrollBy, 20)
72
+ }
73
+ const handlePointerUp = (event: React.MouseEvent<HTMLButtonElement>) => {
74
+ clearInterval(scrollIntervalId)
75
+ }
76
+ const handleKeyDown = (event: React.KeyboardEvent<HTMLButtonElement> ) => {
77
+ if(event.key === "Space" || event.key === "Enter") {
78
+ clearInterval(scrollIntervalId)
79
+ scrollIntervalId = setInterval(handleScrollBy, 20)
80
+ return
81
+ }
82
+ clearInterval(scrollIntervalId)
83
+ }
84
+ const handleKeyUp = (event: React.KeyboardEvent<HTMLButtonElement> ) => {
85
+ clearInterval(scrollIntervalId)
86
+ }
87
+
88
+
89
+
90
+ return (
91
+ <button
92
+ type="button" ref={mergeRefs([ref, internalRef])} data-position={left ? "left":"right"}
93
+ onPointerDown={handlePointerDown} onPointerUp={handlePointerUp} onKeyDown={handleKeyDown} onKeyUp={handleKeyUp}
94
+ className={mergeClassnames("sg-tabs-button", "sg-tabs-scroll-button", className)}
95
+ {...restProps} >
96
+ <span className="visually-hidden">Scroll tab controls {left ? "left":"right"}</span>
97
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256">
98
+ <polyline
99
+ points={left ? "144 88 104 128 144 168" : "112 88 152 128 112 168" } //168 64 104 128 168 192
100
+ fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="32"
101
+ />
102
+ </svg>
103
+ </button>
104
+ )
105
+ })
106
+
107
+ const Controls = forwardRef<HTMLDivElement, TabsControlsType>( ({children, className, ...restProps}, ref) => {
108
+ const { controlId, activeClass } = useTabsContext()
109
+ const [ isOverflow, setIsOverflow ] = useState(false)
110
+ const internalRef = useRef<HTMLDivElement>(null)
111
+
112
+ const checkWidthOverflow = () => {
113
+ if(!internalRef.current) return
114
+
115
+ const el = internalRef.current
116
+ setIsOverflow(el.clientWidth < el.scrollWidth)
117
+ }
118
+ useLayoutEffect(() => {
119
+ console.log(internalRef.current)
120
+ checkWidthOverflow()
121
+ }, [children])
122
+ useEffect(() => {
123
+ window.addEventListener("resize", checkWidthOverflow, true)
124
+ return function cleanup() {
125
+ window.removeEventListener("resize", checkWidthOverflow, true)
126
+ }
127
+ }, [])
128
+
129
+ const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
130
+ const key = event.key
131
+ const tabsControls = internalRef.current
132
+ if(tabsControls) {
133
+ const tabControlsChildren = Array.from(tabsControls.children) as HTMLElement[]
134
+ let newIndex
135
+ if(key === "ArrowRight" || key === "ArrowLeft") {
136
+ event.preventDefault()
137
+ const activeTab = document.querySelector(".sg-tabs-button"+"."+activeClass) as HTMLElement
138
+ const activeTabIndex = tabControlsChildren.indexOf(activeTab)
139
+ const indexChange = key === "ArrowRight" ? 1 : -1
140
+ newIndex = activeTabIndex + indexChange < 0 ? tabControlsChildren.length - 1 : (activeTabIndex + indexChange >= tabControlsChildren.length ? 0 : activeTabIndex + indexChange)
141
+ } else if (key === "Home" || key === "End") {
142
+ event.preventDefault()
143
+ newIndex = key === "Home" ? 0 : tabControlsChildren.length -1
144
+ }
145
+ if(!newIndex) return
146
+ tabControlsChildren[newIndex].focus()
147
+ tabControlsChildren[newIndex].click()
148
+ }
149
+ }
150
+ //div style={{maxWidth:"100%", display:"flex", overflowX:"auto"}}
151
+ return (
152
+ <div tabIndex={-1} onKeyDown={(event) => handleKeyDown(event)} role="tablist" id={controlId+"-tab-controls"} ref={mergeRefs([ref, internalRef])} className={`sg-tabs-controls${className ? " "+className:""}`} {...restProps}>
153
+ {isOverflow ? <ScrollButton controlsRef={internalRef} /> : null }
154
+ {children}
155
+ {isOverflow ? <ScrollButton controlsRef={internalRef} left={false}/> : null }
156
+ </div>
157
+ )
158
+ })
159
+ Controls.displayName = "TabsControl"
160
+
161
+ const Button = forwardRef<HTMLButtonElement, TabsButtonType>( ({children, className, onClick, tabId, id,...restProps}, ref) => {
162
+ const { activeTab, setActiveTab, activeClass, onTabChange } = useTabsContext()
163
+ const isActiveTab = activeTab === tabId
164
+ const classNameComputed = "sg-tabs-button" + (className ? " "+className:"") + (isActiveTab ? " "+activeClass : "")
165
+ const handleClick = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
166
+ if(onTabChange) {
167
+ onTabChange(tabId)
168
+ }
169
+ else {
170
+ setActiveTab(tabId)
171
+ }
172
+ if(onClick) {
173
+ onClick(event)
174
+ }
175
+ }
176
+ return (
177
+ <button
178
+ role="tab" type="button" id={tabId+"-button"} ref={ref} onClick={event => handleClick(event)} className={classNameComputed} {...restProps}
179
+ tabIndex={isActiveTab ? 0:-1} aria-selected={isActiveTab ? "true":"false"} aria-controls={tabId+"-page"}
180
+ >
181
+ {children}
182
+ </button>
183
+ )
184
+ })
185
+ Button.displayName = "TabsButton"
186
+
187
+ const Content = forwardRef<HTMLDivElement, TabsContentType>( ({children, className, ...restProps}, ref) => {
188
+ return (
189
+ <div role="none" ref={ref} className={`sg-tabs-content${className ? " "+className:""}`} {...restProps}>
190
+ {children}
191
+ </div>
192
+ )
193
+ })
194
+ Content.displayName = "TabsContent"
195
+
196
+ const Page = forwardRef<HTMLDivElement, TabsPageType>( ({children, className, tabId, ...restProps}, ref) => {
197
+ const { activeTab, activeClass } = useTabsContext()
198
+ const classNameComputed = "sg-tabs-page" + (className ? " "+className:"") + (activeTab === tabId ? " "+activeClass : "")
199
+ return (
200
+ <div
201
+ role="tabpanel" id={tabId+"-page"} aria-labelledby={tabId+"-button"}
202
+ ref={ref} className={classNameComputed} {...restProps}
203
+ >
204
+ {children}
205
+ </div>
206
+ )
207
+ })
208
+ Page.displayName = "TabsPage"
209
+
210
+ export default Object.assign(Tabs, {
211
+ Controls: Controls,
212
+ Button: Button,
213
+ Content: Content,
214
+ Page: Page,
215
+ })
@@ -0,0 +1,49 @@
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
+ }
10
+
11
+ export type TabsType = {
12
+ children: React.ReactNode,
13
+ className?: string,
14
+ controlId: string,
15
+ activeClassName?: string,
16
+ defaultActive: string,
17
+ active?: string,
18
+ onTabChange?: Function
19
+ } & BaseDivType
20
+
21
+ export type TabsControlsType = {
22
+ children: React.ReactNode,
23
+ className?: string,
24
+ } & BaseDivType
25
+
26
+ export type TabsScrollButtonType = {
27
+ className?: string,
28
+ left?: boolean,
29
+ controlsRef?: React.RefObject<HTMLDivElement>
30
+ } & BaseButtonType
31
+
32
+ export type TabsButtonType = {
33
+ children: React.ReactNode,
34
+ className?: string,
35
+ onClick?: Function,
36
+ tabId: string,
37
+ id?: string,
38
+ } & BaseButtonType
39
+
40
+ export type TabsContentType = {
41
+ children: React.ReactNode,
42
+ className?: string,
43
+ } & BaseDivType
44
+
45
+ export type TabsPageType = {
46
+ children: React.ReactNode,
47
+ className?: string,
48
+ tabId: string,
49
+ } & 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"
@@ -1,5 +1,6 @@
1
- import useClassname from "./useClassname";
2
- import useDraggable from "./useDraggable";
3
- import useScreenSize from "./useScreenSize";
4
- import useKeepElementFocused from "./useKeepElementFocused";
5
- export { useClassname, useDraggable, useScreenSize, useKeepElementFocused };
1
+ import useClassname from "./useClassname";
2
+ import useDraggable from "./useDraggable";
3
+ import useScreenSize from "./useScreenSize";
4
+ import useKeepElementFocused from "./useKeepElementFocused";
5
+
6
+ export {useClassname, useDraggable, useScreenSize, useKeepElementFocused}
@@ -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,24 @@
1
+ import { useState, useMemo, useEffect} from "react"
2
+ import { debounce } from "lodash"
3
+
4
+ const useScreenSize = (debounceTime=25) => {
5
+ const [size, setSize]= useState({height: window.innerHeight, width: window.innerWidth})
6
+
7
+
8
+ const handleResize = ()=> {
9
+ setSize({height: window.innerHeight, width: window.innerWidth})
10
+ }
11
+ const handleResizeDebounced = useMemo(() =>
12
+ debounce(handleResize, debounceTime)
13
+ , [debounceTime] )
14
+
15
+ useEffect(() => {
16
+ window.addEventListener("resize", handleResizeDebounced, true)
17
+ return function cleanup() {
18
+ window.removeEventListener("resize", handleResizeDebounced, true)
19
+ }
20
+ }, [handleResizeDebounced])
21
+ return size
22
+ }
23
+
24
+ export default useScreenSize
package/src/index.ts ADDED
@@ -0,0 +1,21 @@
1
+ export * from "./Button"
2
+ export * from "./ButtonGroup"
3
+ export * from "./Card"
4
+ export * from "./CloseButton"
5
+ export * from "./Dropdown"
6
+ export * from "./FloatingLabel"
7
+ export * from "./Form"
8
+ //export * from "./Grid"
9
+ export * from "./InputGroup"
10
+ export * from "./Modal"
11
+ export * from "./Nav"
12
+ export * from "./NavBar"
13
+ export * from "./NavDropdown"
14
+ //export * from "./OffCanvas"
15
+ //export * from "./Overlay"
16
+ export * from "./Popout"
17
+ export * from "./Spinner"
18
+ export * from "./Table"
19
+ export * from "./Tabs"
20
+ export * from "./ToggleButton"
21
+ export * from "./utils/BaseTypes"