stargazer-ui 1.5.7 → 1.5.9

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 (369) 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 +74 -0
  8. package/dev/pages/ButtonPage.jsx +44 -0
  9. package/dev/pages/CardPage.jsx +81 -0
  10. package/dev/pages/DropdownPage.jsx +48 -0
  11. package/dev/pages/FormPage.jsx +156 -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 +420 -0
  38. package/src/Dropdown/Dropdown.types.ts +57 -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 +550 -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 +121 -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 → types/components/Dropdown}/Dropdown.d.ts +1 -5
  149. package/{Dropdown → types/components/Dropdown}/Dropdown.types.d.ts +10 -4
  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/{NavDropdown → types/components/NavDropdown}/NavDropdown.d.ts +2 -4
  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.js +0 -352
  214. package/Dropdown/Dropdown.js.map +0 -1
  215. package/Dropdown/index.d.ts +0 -4
  216. package/Dropdown/index.js +0 -8
  217. package/Dropdown/index.js.map +0 -1
  218. package/Dropdown/package.json +0 -1
  219. package/FileUploadButton/FileUploadButton.d.ts +0 -4
  220. package/FileUploadButton/FileUploadButton.js +0 -20
  221. package/FileUploadButton/FileUploadButton.js.map +0 -1
  222. package/FileUploadButton/FileUploadButton.types.d.ts +0 -7
  223. package/FileUploadButton/index.d.ts +0 -3
  224. package/FileUploadButton/index.js +0 -7
  225. package/FileUploadButton/index.js.map +0 -1
  226. package/FileUploadButton/package.json +0 -1
  227. package/FloatingLabel/FloatingLabel.js +0 -15
  228. package/FloatingLabel/FloatingLabel.js.map +0 -1
  229. package/FloatingLabel/index.d.ts +0 -3
  230. package/FloatingLabel/index.js +0 -7
  231. package/FloatingLabel/index.js.map +0 -1
  232. package/FloatingLabel/package.json +0 -1
  233. package/Form/Form.d.ts +0 -38
  234. package/Form/Form.js +0 -227
  235. package/Form/Form.js.map +0 -1
  236. package/Form/Form.types.d.ts +0 -158
  237. package/Form/FormSelect.d.ts +0 -15
  238. package/Form/FormSelect.js +0 -441
  239. package/Form/FormSelect.js.map +0 -1
  240. package/Form/index.d.ts +0 -4
  241. package/Form/index.js +0 -8
  242. package/Form/index.js.map +0 -1
  243. package/Form/package.json +0 -1
  244. package/InputGroup/InputGroup.d.ts +0 -7
  245. package/InputGroup/InputGroup.js +0 -31
  246. package/InputGroup/InputGroup.js.map +0 -1
  247. package/InputGroup/InputGroup.types.d.ts +0 -17
  248. package/InputGroup/index.d.ts +0 -4
  249. package/InputGroup/index.js +0 -7
  250. package/InputGroup/index.js.map +0 -1
  251. package/InputGroup/package.json +0 -1
  252. package/List/List.d.ts +0 -14
  253. package/List/List.js +0 -77
  254. package/List/List.js.map +0 -1
  255. package/List/List.types.d.ts +0 -28
  256. package/List/index.d.ts +0 -3
  257. package/List/index.js +0 -7
  258. package/List/index.js.map +0 -1
  259. package/List/package.json +0 -1
  260. package/Modal/Modal.js +0 -157
  261. package/Modal/Modal.js.map +0 -1
  262. package/Modal/index.d.ts +0 -3
  263. package/Modal/index.js +0 -7
  264. package/Modal/index.js.map +0 -1
  265. package/Modal/package.json +0 -1
  266. package/Nav/Nav.js +0 -29
  267. package/Nav/Nav.js.map +0 -1
  268. package/Nav/index.d.ts +0 -4
  269. package/Nav/index.js +0 -7
  270. package/Nav/index.js.map +0 -1
  271. package/Nav/package.json +0 -1
  272. package/NavBar/Navbar.js +0 -36
  273. package/NavBar/Navbar.js.map +0 -1
  274. package/NavBar/index.d.ts +0 -4
  275. package/NavBar/index.js +0 -8
  276. package/NavBar/index.js.map +0 -1
  277. package/NavBar/package.json +0 -1
  278. package/NavDropdown/NavDropdown.js +0 -75
  279. package/NavDropdown/NavDropdown.js.map +0 -1
  280. package/NavDropdown/index.d.ts +0 -3
  281. package/NavDropdown/index.js +0 -7
  282. package/NavDropdown/index.js.map +0 -1
  283. package/NavDropdown/package.json +0 -1
  284. package/Overlay/Overlay.d.ts +0 -4
  285. package/Overlay/Overlay.js +0 -242
  286. package/Overlay/Overlay.js.map +0 -1
  287. package/Overlay/Overlay.types.d.ts +0 -22
  288. package/Overlay/index.js +0 -7
  289. package/Overlay/index.js.map +0 -1
  290. package/Overlay/package.json +0 -1
  291. package/Popout/Popout.js +0 -111
  292. package/Popout/Popout.js.map +0 -1
  293. package/Popout/index.d.ts +0 -3
  294. package/Popout/index.js +0 -7
  295. package/Popout/index.js.map +0 -1
  296. package/Popout/package.json +0 -1
  297. package/Spinner/Spinner.js +0 -11
  298. package/Spinner/Spinner.js.map +0 -1
  299. package/Spinner/index.d.ts +0 -3
  300. package/Spinner/index.js +0 -7
  301. package/Spinner/index.js.map +0 -1
  302. package/Spinner/package.json +0 -1
  303. package/Table/Table.js +0 -12
  304. package/Table/Table.js.map +0 -1
  305. package/Table/index.d.ts +0 -3
  306. package/Table/index.js +0 -7
  307. package/Table/index.js.map +0 -1
  308. package/Table/package.json +0 -1
  309. package/Tabs/Tabs.js +0 -162
  310. package/Tabs/Tabs.js.map +0 -1
  311. package/Tabs/index.d.ts +0 -3
  312. package/Tabs/index.js +0 -7
  313. package/Tabs/index.js.map +0 -1
  314. package/Tabs/package.json +0 -1
  315. package/ToggleButton/ToggleButton.d.ts +0 -4
  316. package/ToggleButton/ToggleButton.js +0 -18
  317. package/ToggleButton/ToggleButton.js.map +0 -1
  318. package/ToggleButton/ToggleButton.types.d.ts +0 -7
  319. package/ToggleButton/index.d.ts +0 -3
  320. package/ToggleButton/index.js +0 -7
  321. package/ToggleButton/index.js.map +0 -1
  322. package/ToggleButton/package.json +0 -1
  323. package/hooks/index.js +0 -7
  324. package/hooks/index.js.map +0 -1
  325. package/hooks/package.json +0 -1
  326. package/hooks/useClassname.d.ts +0 -2
  327. package/hooks/useClassname.js +0 -7
  328. package/hooks/useClassname.js.map +0 -1
  329. package/hooks/useDraggable.d.ts +0 -23
  330. package/hooks/useDraggable.js +0 -147
  331. package/hooks/useDraggable.js.map +0 -1
  332. package/hooks/useKeepElementFocused.d.ts +0 -2
  333. package/hooks/useKeepElementFocused.js +0 -37
  334. package/hooks/useKeepElementFocused.js.map +0 -1
  335. package/hooks/useQueryParams.d.ts +0 -2
  336. package/hooks/useQueryParams.js +0 -13
  337. package/hooks/useQueryParams.js.map +0 -1
  338. package/hooks/useScreenSize.d.ts +0 -5
  339. package/hooks/useScreenSize.js +0 -21
  340. package/hooks/useScreenSize.js.map +0 -1
  341. package/index.d.ts +0 -18
  342. package/index.js +0 -19
  343. package/index.js.map +0 -1
  344. package/styles/stargazerui.css +0 -6548
  345. package/styles/stargazerui.css.map +0 -1
  346. package/utils/ContrastingColor.d.ts +0 -1
  347. package/utils/CreateSyntheticEvent.d.ts +0 -3
  348. package/utils/CreateSyntheticEvent.js +0 -33
  349. package/utils/CreateSyntheticEvent.js.map +0 -1
  350. package/utils/IsInputKey.d.ts +0 -7
  351. package/utils/IsInputKey.js +0 -20
  352. package/utils/IsInputKey.js.map +0 -1
  353. package/utils/MergeClassnames.d.ts +0 -2
  354. package/utils/MergeClassnames.js +0 -7
  355. package/utils/MergeClassnames.js.map +0 -1
  356. package/utils/MergeRefs.d.ts +0 -2
  357. package/utils/MergeRefs.js +0 -16
  358. package/utils/MergeRefs.js.map +0 -1
  359. /package/{Button → types/components/Button}/Button.d.ts +0 -0
  360. /package/{Card → types/components/Card}/Card.d.ts +0 -0
  361. /package/{CloseButton → types/components/CloseButton}/CloseButton.d.ts +0 -0
  362. /package/{FloatingLabel → types/components/FloatingLabel}/FloatingLabel.d.ts +0 -0
  363. /package/{Nav → types/components/Nav}/Nav.d.ts +0 -0
  364. /package/{NavDropdown → types/components/NavDropdown}/NavDropdown.types.d.ts +0 -0
  365. /package/{Popout → types/components/Popout}/Popout.d.ts +0 -0
  366. /package/{Spinner → types/components/Spinner}/Spinner.d.ts +0 -0
  367. /package/{Spinner → types/components/Spinner}/Spinner.types.d.ts +0 -0
  368. /package/{Table → types/components/Table}/Table.d.ts +0 -0
  369. /package/{Tabs → types/components/Tabs}/Tabs.d.ts +0 -0
@@ -0,0 +1,550 @@
1
+ import { forwardRef, useState, useEffect, useLayoutEffect, useRef, useMemo, createContext, useContext } from "react"
2
+ import { FormSelectType, FormSelectControlType, FormSelectInputType, FormSelectListType, FormSelectOptionType, SelectContextType } from "./Form.types"
3
+
4
+ import Overlay from "../Overlay"
5
+
6
+ import { useFormContext, useFormTagContext, ErrorMessage, HintMessage } from "./Form"
7
+ import mergeRefs from "../utils/MergeRefs"
8
+ import mergeClassnames from "../utils/MergeClassnames"
9
+ import createSyntheticEvent from "../utils/CreateSyntheticEvent"
10
+ import { InputKeyType, isValidInputKey } from "../utils/IsInputKey"
11
+
12
+ export const SelectContext = createContext<SelectContextType | null>(null)
13
+ export const SelectContextProvider = ({children, value} : {children: React.ReactNode, value:SelectContextType}) => {
14
+ return (
15
+ <SelectContext.Provider value={value}>
16
+ {children}
17
+ </SelectContext.Provider>
18
+ )
19
+ }
20
+ export const useSelectContext = () => {
21
+ const context = useContext(SelectContext)
22
+ if(!context) {
23
+ throw new Error("UseSelectContext must be used within a SelectContextProvider!")
24
+ }
25
+ return context
26
+ }
27
+
28
+ const useCustomState = (test: any, middleware?: any):([any, (callback: any, options?: any) => void]) => {
29
+ const [state, setState] = useState(test)
30
+ const customSetState = (callback: any, options: any = {middleware:true}) => {
31
+ let newValue
32
+ if( (typeof callback) === "function") {
33
+ newValue = callback(state)
34
+ } else {
35
+ newValue = callback
36
+ }
37
+ if(middleware && options.middleware === true) middleware(newValue)
38
+ setState(newValue)
39
+ }
40
+ return [state, customSetState]
41
+ }
42
+
43
+ const inputKeys: InputKeyType[] = [
44
+ {
45
+ id: "ArrowDown",
46
+ alt: true
47
+ },
48
+ {
49
+ id: "ArrowUp",
50
+ alt: true
51
+ },
52
+ { id: "Home" }, { id: "End" }, { id: "Enter" }, { id: "Escape" },
53
+ { id: "PageDown" }, { id: "PageUp" }, { id: " " }, { id: "Tab" }
54
+ ]
55
+
56
+
57
+ const Select = forwardRef<HTMLButtonElement, FormSelectType>( ({
58
+ children, className, id, required=false, disabled=false, value, label,
59
+ errorAsOverlay, error, hint, "aria-describedby":ariaDescribedby, loading=false,
60
+ onClick, onBlur, onKeyUp, onKeyDown, onChange,
61
+ ...restProps
62
+ }, ref) => {
63
+
64
+ if(Array.isArray(children)) {
65
+ children = children.flat(Infinity).filter(child => child !== null && child !== undefined)
66
+ } else {
67
+ children = [children].filter(child => child !== null && child !== undefined)
68
+ }
69
+
70
+
71
+ const { noValidate } = useFormTagContext()
72
+ const { controlId, isInputGroup, isFLoatingLabel } = useFormContext()
73
+
74
+ const isOverlay = isInputGroup || isFLoatingLabel || errorAsOverlay
75
+ const computedClassName = mergeClassnames(
76
+ "sg-form-select", className, error ? "invalid":"", disabled ? "disabled":""
77
+ )
78
+
79
+ let elementId = controlId ?? id
80
+ if(elementId === undefined) {
81
+ throw new Error(
82
+ "Form.Select needs to have an id, either provided directly through the 'id' property or wrapped in a Form.Group with a 'controlId' !"
83
+ )
84
+ }
85
+
86
+ const hasValidChildren = useMemo(() => {
87
+ if(!children || !Array.isArray(children)) return false
88
+ if(children.length < 1) return false
89
+
90
+ let isValid = true
91
+ children.forEach(child => {
92
+ if(child.props.value === undefined || child.props.value === null) {
93
+ isValid = false
94
+ }
95
+ })
96
+
97
+ if(children[0].props.value && children[0].props.value != "") {
98
+ console.warn("It is recommended to have the first select option in a 'Form.Select' to be a placeholder like 'Select option...' with a value of an empty string")
99
+ }
100
+ return isValid
101
+ }, [children])
102
+ if(!hasValidChildren || !Array.isArray(children)) {
103
+ throw new Error(
104
+ "Form.Select needs to have 1 or more 'Form.Select.Option' children, each with value attributes!"
105
+ )
106
+ }
107
+
108
+ const errorMessageId = error ? elementId+"-error-message":undefined
109
+ const hintMessageId = hint ? elementId+"-hint-message":undefined
110
+ const tooltipMessage = isOverlay && (error || hint) ?
111
+ <div className="sg-form-control-description tooltip">
112
+ {error? <ErrorMessage id={errorMessageId} message={error.message} /> : null}
113
+ {hint? <HintMessage id={hintMessageId} message={hint.message} /> : null}
114
+ </div> : undefined// "Testing a tooltip with a long message. This messsage is so long it hsould in theory trigger a wrap."
115
+
116
+ const describedby = mergeClassnames(ariaDescribedby, errorMessageId, hintMessageId)
117
+
118
+ const internalSelectControlRef = useRef<HTMLButtonElement>(null)
119
+ const [ showList, setShowList ] = useState<boolean>(false)
120
+
121
+ const initialValue = value ?
122
+ {
123
+ value: value,
124
+ label: children.find(child => child.props.value === value) ? children.find(child => child.props.value === value).props.children : "no label found"
125
+ }:
126
+ {
127
+ value: children[0].props.value ?? "",
128
+ label: children[0].props.children ?? "no label found"
129
+ }
130
+ const handleChange = (newValue: any) => {
131
+ if(!internalSelectControlRef.current) return
132
+
133
+ const target = internalSelectControlRef.current
134
+ target.value = newValue.value;
135
+ const event = new Event('change', { bubbles: true });
136
+ Object.defineProperty(event, 'target', { writable: false, value: target })
137
+ const syntheticEvent = createSyntheticEvent(event) as React.ChangeEvent<typeof target>;
138
+ if(onChange) onChange(syntheticEvent);
139
+ }
140
+ const [ activeDescendant, setActiveDescendant ] = useState<any>(initialValue.value)
141
+ const [ selectedDescendant, setSelectedDescendant ] = useCustomState(initialValue, handleChange)
142
+ const [ inputValue, setInputValue ] = useState<string>("")
143
+
144
+ if(!loading) {
145
+ const isChild = children.find(child => child.props.value === value)
146
+ const isDefaultChild = children.find(child => child.props.value === "")
147
+ const computedLabel = isChild ? isChild.props.children : (isDefaultChild ? isDefaultChild.props.children : "no label found")
148
+ if(selectedDescendant.label != computedLabel) {
149
+ setSelectedDescendant((prev: any) => ({
150
+ ...prev,
151
+ label: computedLabel
152
+ }), {middleware: false})
153
+ }
154
+ }
155
+
156
+ if(value != undefined && value != null && value !== selectedDescendant.value && !loading) {
157
+ const isChild = children.find(child => child.props.value === value)
158
+ const isDefaultChild = children.find(child => child.props.value === "")
159
+ const computedLabel = isChild ? isChild.props.children : (isDefaultChild ? isDefaultChild.props.children : "no label found")
160
+ const computedValue = isChild ? value : ""
161
+ console.log(value, computedLabel)
162
+ setSelectedDescendant({
163
+ value: value,
164
+ label: computedLabel
165
+ }, {middleware: false})
166
+ }
167
+
168
+ const resetFocus = () => {
169
+ setActiveDescendant(selectedDescendant.value)
170
+ }
171
+ const handleSetShowList = (show: boolean | ((show:boolean) => boolean)) => {
172
+ if(!showList) {
173
+ resetFocus()
174
+ }
175
+ setShowList(show)
176
+ }
177
+
178
+ const handleClick = (event?: React.MouseEvent<HTMLButtonElement>) => {
179
+ handleSetShowList(prev => !prev)
180
+ /*
181
+ if(!showList) {
182
+ const firstChildValue = children[0].props.value ?? ""
183
+ console.log(firstChildValue)
184
+ setActiveDescendant(firstChildValue)
185
+ }
186
+ */
187
+ if(onClick && event) onClick(event)
188
+ }
189
+ const handleBlur = (event: React.FocusEvent<HTMLButtonElement>, hasOnBlur=true) => {
190
+ handleSetShowList(false)
191
+ if(onBlur && hasOnBlur) onBlur(event)
192
+ }
193
+ useEffect(() => {
194
+ const select = internalSelectControlRef.current
195
+ window.addEventListener("pointerdown", event => {
196
+ if(!select?.contains(event.target as HTMLElement)) handleSetShowList(false)
197
+ }, true)
198
+ window.addEventListener("resize", event => handleSetShowList(false), true)
199
+ return function cleanup () {
200
+ window.removeEventListener("pointerdown", event => {
201
+ if(select?.contains(event.target as HTMLElement)) handleSetShowList(false)
202
+ }, true)
203
+ window.removeEventListener("resize", event => handleSetShowList(false), true)
204
+ }
205
+ }, [])
206
+
207
+ const changeActiveDescendant = (number: number, type: string) => {
208
+ const maxIndex = (children as any).length - 1
209
+ const minIndex = 0
210
+ const currentIndex = (children as any).indexOf((children as any).find((child:any) => child.props.value === activeDescendant))
211
+ let newIndex, temp
212
+ switch (type) {
213
+ case "add":
214
+ temp = currentIndex + number
215
+ newIndex = temp > maxIndex ? maxIndex : temp
216
+ break
217
+ case "sub":
218
+ temp = currentIndex - number
219
+ newIndex = temp < minIndex ? minIndex : temp
220
+ break
221
+ case "set":
222
+ newIndex = number > maxIndex ? maxIndex : (number < minIndex ? 0 : number)
223
+ break
224
+ default:
225
+ newIndex = 0
226
+ break
227
+ }
228
+ setActiveDescendant((children as any)[newIndex].props.value)
229
+ }
230
+ const handleKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {
231
+ const isInputKey = isValidInputKey(event, inputKeys)
232
+ if(onKeyDown) onKeyDown(event)
233
+ if(!isInputKey) return
234
+
235
+ let isPreventDefault = true
236
+ let isStopPropagation = true
237
+
238
+ switch (event.key) {
239
+ case "ArrowDown":
240
+ if(showList) changeActiveDescendant(1, "add")
241
+ break
242
+ case "ArrowUp":
243
+ if(showList) changeActiveDescendant(1, "sub")
244
+ break
245
+ case "Tab":
246
+ if(!showList) {
247
+ isPreventDefault = false
248
+ isStopPropagation = false
249
+ }
250
+ }
251
+ if(isPreventDefault) event.preventDefault()
252
+ if(isStopPropagation) event.stopPropagation()
253
+ }
254
+ const handleKeyUp = (event: React.KeyboardEvent<HTMLButtonElement>) => {
255
+ const isInputKey = isValidInputKey(event, inputKeys)
256
+ if(onKeyUp) onKeyUp(event)
257
+ if(!isInputKey) return
258
+
259
+ let isPreventDefault = true
260
+ let isStopPropagation = true
261
+ switch(event.key) {
262
+ case "ArrowDown":
263
+ if(!showList) {
264
+ handleSetShowList(true)
265
+ }
266
+ break
267
+ case "ArrowUp":
268
+ if(!showList) {
269
+ handleSetShowList(true)
270
+ changeActiveDescendant(0, "set")
271
+ }
272
+ break
273
+ case "Home":
274
+ if(!showList) {
275
+ handleSetShowList(true)
276
+ }
277
+ changeActiveDescendant(0, "set")
278
+ break
279
+ case "End":
280
+ if(!showList) {
281
+ handleSetShowList(true)
282
+ }
283
+ changeActiveDescendant((children as any).length - 1, "set")
284
+ break
285
+ case "Enter":
286
+ case " ":
287
+ handleClick()
288
+ if(showList) {
289
+ const focusElement = document.getElementById(elementId+"-list-item-"+activeDescendant)
290
+ focusElement?.click()
291
+ }
292
+ break
293
+ case "Escape":
294
+ if(showList) {
295
+ handleSetShowList(false)
296
+ }
297
+ break
298
+ case "PageDown":
299
+ changeActiveDescendant(10, "add")
300
+ break
301
+ case "PageUp":
302
+ changeActiveDescendant(10, "sub")
303
+ break
304
+ case "Tab":
305
+ if(showList) {
306
+ handleClick()
307
+ const focusElement = document.getElementById(elementId+"-list-item-"+activeDescendant)
308
+ focusElement?.click()
309
+ }
310
+ isPreventDefault = false
311
+ isStopPropagation = false
312
+ break
313
+ }
314
+ if(event.key === "Tab") console.log(isPreventDefault)
315
+ if(isPreventDefault) event.preventDefault()
316
+ if(isStopPropagation) event.stopPropagation()
317
+ }
318
+
319
+ const context = useMemo(() => ({
320
+ internalId: elementId,
321
+ showList,
322
+ setShowList: handleSetShowList,
323
+ activeDescendant,
324
+ setActiveDescendant,
325
+ inputValue,
326
+ setInputValue,
327
+ selectedDescendant,
328
+ setSelectedDescendant,
329
+ children,
330
+ handleBlur
331
+ }), [elementId, showList, activeDescendant, inputValue ])
332
+
333
+ return (
334
+ <SelectContextProvider value={context}>
335
+ <Overlay trigger={"focus"} position="bottom" tooltip={tooltipMessage}>
336
+ <SelectControl
337
+ value={selectedDescendant.value} label={selectedDescendant.label}
338
+ className={computedClassName} id={elementId} required={(required && !noValidate) ?? undefined} disabled={disabled}
339
+ aria-required={required ?? undefined} aria-invalid={error ? "true":"false"} aria-describedby={describedby != "" ? describedby : undefined}
340
+ onClick={handleClick} onBlur={handleBlur} onKeyDown={handleKeyDown} onKeyUp={handleKeyUp}
341
+ {...restProps} ref={mergeRefs([ref, internalSelectControlRef])}
342
+ >
343
+ <SelectList>
344
+ {!loading ?
345
+ children
346
+ :
347
+ <SelectOption disabled={true} key="loading" value="">
348
+ Loading...
349
+ </SelectOption>
350
+ }
351
+ </SelectList>
352
+ </SelectControl>
353
+ </Overlay>
354
+ <div className="sg-form-control-description">
355
+ {error && !isOverlay ?
356
+ <ErrorMessage id={elementId} message={error.message} style={error.style} className={error.className}/>
357
+ : null }
358
+ {hint && !isOverlay ?
359
+ <HintMessage id={elementId} message={hint.message} style={hint.style} className={hint.className}/>
360
+ : null }
361
+ </div>
362
+ </SelectContextProvider>
363
+ )
364
+ })
365
+ Select.displayName = "FormSelect"
366
+
367
+ const SelectInput = forwardRef<HTMLInputElement, FormSelectInputType>( ({className, id, value, onChange, ...restProps}, ref ) => {
368
+ const { showList, setShowList, inputValue, setInputValue, internalId } = useSelectContext()
369
+
370
+ const debouncedInput = (value: string) => {
371
+ setTimeout(()=>{
372
+ setInputValue(value)
373
+ }, 300)
374
+ }
375
+ const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
376
+ console.log(event)
377
+ if(!showList) { setShowList(true) }
378
+ if(onChange) { onChange(event) }
379
+ //debouncedInput(event.target.value)
380
+ setInputValue(event.target.value)
381
+ }
382
+ const handleKeyUp = (event: React.KeyboardEvent) => {
383
+ const key = event.key
384
+ switch(key) {
385
+ case "ArrowDown":
386
+ console.log("down arrow")
387
+ break
388
+ case "ArrowUp":
389
+ console.log("up arrow")
390
+ break
391
+ }
392
+ }
393
+
394
+
395
+ return (
396
+ <input value={inputValue} ref={ref}
397
+ onKeyUp={handleKeyUp} onChange={handleChange}
398
+ className={mergeClassnames("sg-select-input", className)}
399
+ {...restProps}
400
+ />
401
+ )
402
+ })
403
+ SelectInput.displayName = "FormSelectInput"
404
+
405
+ const SelectControl = forwardRef<HTMLButtonElement, FormSelectControlType>( ({children, className, value, label, searchable=false, required=false, onChange, ...restProps}, ref) => {
406
+ const { activeDescendant, showList, internalId } = useSelectContext()
407
+ const internalButtonRef = useRef<HTMLButtonElement>(null)
408
+
409
+ const computedClassName = mergeClassnames(className, "sg-select-control")
410
+
411
+ return (
412
+ <button
413
+ role="combobox" aria-controls={internalId+"list"} aria-expanded={showList} aria-activedescendant={activeDescendant === false ? "":internalId+"-list-item-"+activeDescendant}
414
+ ref={mergeRefs([ref, internalButtonRef])} value={value} type="button"
415
+ className={computedClassName} id={internalId+"-control"}
416
+ {...restProps}
417
+ >
418
+ <span>{label}</span>
419
+ {children}
420
+ </button>
421
+ )
422
+ })
423
+ SelectControl.displayName = "FormSelectControl"
424
+ /*
425
+ const SelectControlAlternate = forwardRef<HTMLDivElement, FormSelectControlType>( ({children, className, placeholder="Placeholder...", searchable=false, inputRef, inputOptions, ...restProps}, ref) => {
426
+ const { setShowList, internalId } = useSelectContext()
427
+ const selectControlRef = useRef<HTMLDivElement>(null)
428
+
429
+ const handlePointerUp = (event: React.MouseEvent) => {
430
+ setShowList(prev => !prev);
431
+ const selectControl = selectControlRef.current
432
+ if(selectControl) {
433
+ selectControl.classList.add("focus")
434
+ const eventTargetChildren = selectControl.children[0] as HTMLElement
435
+ if(eventTargetChildren) { eventTargetChildren.focus() }
436
+ }
437
+ }
438
+ const inputParams = {...inputOptions, disabled:searchable, placeholder:placeholder, ref:inputRef}
439
+
440
+ return (
441
+ <div ref={mergeRefs([ref, selectControlRef])} onPointerUp={handlePointerUp} className={mergeClassnames("sg-select-control", className)} {...restProps}>
442
+ <SelectInput {...inputParams}/>
443
+ </div>
444
+ )
445
+ })
446
+ SelectControl.displayName = "FormSelectControl"
447
+ */
448
+ const listPositionSetter = (listRef: any) => {
449
+ const listElement = listRef.current
450
+ if(!listElement) return
451
+ const parent = listElement.parentElement
452
+ if(!parent) return
453
+ let position: React.CSSProperties = {}
454
+ const {height: listHeight } = listElement.getBoundingClientRect()
455
+ const { top, bottom} = parent.getBoundingClientRect()
456
+ const {innerHeight} = window
457
+
458
+ const isTop = top > listHeight
459
+ const isBottom = innerHeight - bottom > listHeight
460
+ const coordinate = "calc(100% + 4px)"
461
+ if(isBottom) {
462
+ position = {top: coordinate, bottom: "unset"}
463
+ } else if (isTop) {
464
+ position = {bottom:coordinate, top: "unset"}
465
+ } else {
466
+ const height = innerHeight - bottom - 2
467
+ position = {top: coordinate, maxHeight: height}
468
+ }
469
+ if(!listElement.children) return
470
+ const listChildren = listElement.children
471
+ const numberChildren = listChildren.length
472
+ const numberOfRenderedChildren = numberChildren > 5 ? 5 : numberChildren
473
+ let renderedListHeight = 0
474
+ for (let i=0; i< numberOfRenderedChildren; i++) {
475
+ renderedListHeight += listChildren[i].getBoundingClientRect().height
476
+ }
477
+ renderedListHeight += 4
478
+ position.height = renderedListHeight ?? "auto"
479
+
480
+ return position
481
+ }
482
+
483
+ const SelectList = forwardRef<HTMLUListElement, FormSelectListType>( ({children, className, id, ...restProps}, ref) => {
484
+ const { showList, internalId } = useSelectContext()
485
+
486
+ const [computedStyle, setComputedStyle] = useState<React.CSSProperties>({})
487
+
488
+ const listRef = useRef<HTMLUListElement>(null)
489
+ useLayoutEffect(() => {
490
+ if(!showList) {
491
+ return
492
+ }
493
+ const newPosition = listPositionSetter(listRef)
494
+ setComputedStyle(newPosition!)
495
+ }, [showList, listRef.current])
496
+
497
+ return (
498
+ <ul
499
+ role="listbox" ref={mergeRefs([ref, listRef])} id={internalId+"-list"}
500
+ className={mergeClassnames("sg-select-list", className)} style={showList ? {...computedStyle} : {display:"none"}}
501
+ {...restProps}
502
+ >
503
+ {children}
504
+ </ul>
505
+ )
506
+ })
507
+ SelectList.displayName = "FormSelectList"
508
+
509
+ const SelectOption = forwardRef<HTMLLIElement, FormSelectOptionType>(({
510
+ children, className, id, value, disabled, label, selected,
511
+ onPointerDown, onPointerOver, onClick, ...restProps
512
+ }, ref) => {
513
+ const { internalId, activeDescendant, setActiveDescendant, selectedDescendant, setSelectedDescendant, setShowList } = useSelectContext()
514
+ const handlePointerEnter = (event: React.PointerEvent<HTMLLIElement>) => {
515
+ if(!event.target) return
516
+ setActiveDescendant(value)
517
+ if(onPointerOver) onPointerOver(event)
518
+ }
519
+ const handleCLick = (event: React.MouseEvent<HTMLLIElement>) => {
520
+ event.stopPropagation()
521
+ if(disabled) return
522
+
523
+ setSelectedDescendant( (prev:any) => ({
524
+ value: value,
525
+ label: children
526
+ }))
527
+ setShowList(false)
528
+
529
+ if(onClick) onClick(event)
530
+ }
531
+
532
+ const computedClassName = mergeClassnames("sg-select-list-item", className, activeDescendant === value ? "focus":"")
533
+ return (
534
+ <li role="option" aria-selected={selectedDescendant.value === value ? true:false}
535
+ ref={ref} id={internalId+"-list-item-"+value} className={computedClassName}
536
+ onPointerOver={handlePointerEnter} onClick={handleCLick}
537
+ {...restProps}
538
+ >
539
+ {children}
540
+ </li>
541
+ )
542
+ })
543
+ SelectOption.displayName = "FormSelectOption"
544
+
545
+ export default Object.assign(Select, {
546
+ Control: SelectControl,
547
+ Input: SelectInput,
548
+ Options: SelectList,
549
+ Option: SelectOption
550
+ })
@@ -0,0 +1,4 @@
1
+ import Form from "./Form"
2
+ export default Form
3
+ export {FormContextProvider, useFormContext} from "./Form"
4
+ export type {FormCheckType, FormContextType, FormControlType, FormGroupType, FormLabelType, FormSelectType, FormTextType, FormType} from "./Form.types"
@@ -0,0 +1,46 @@
1
+ import React, { forwardRef, useMemo } from "react"
2
+
3
+ import { InputGroupText, InputGroupType, InputGroupGridType } from "./InputGroup.types"
4
+
5
+ import { FormContextProvider, useFormContext } from "../Form"
6
+ import { useClassname } from "../hooks"
7
+
8
+ const InputGroup = forwardRef<HTMLDivElement, InputGroupType>(({children, className, controlId, ...restProps}, ref) => {
9
+ const context = useMemo(() => {
10
+ return {controlId: controlId, isInputGroup: true}
11
+ }, [controlId])
12
+ return (
13
+ <div ref={ref} className={useClassname("sg-input-group", className)} {...restProps}>
14
+ <FormContextProvider value={context}>
15
+ {children}
16
+ </FormContextProvider>
17
+ </div>
18
+ )
19
+ })
20
+ InputGroup.displayName = "InputGroup"
21
+
22
+ const Grid = forwardRef<HTMLDivElement, InputGroupGridType>( ({children, className, ...restProps}, ref) => {
23
+ return (
24
+ <div ref={ref} className={useClassname("sg-input-group-grid", className)} {...restProps}>
25
+ {children}
26
+ </div>
27
+ )
28
+ })
29
+ Grid.displayName = "InputGroupGrid"
30
+
31
+ const Text = forwardRef<HTMLLabelElement, InputGroupText>( ({children, className, htmlFor, ...restProps}, ref) => {
32
+ const { controlId } = useFormContext()
33
+
34
+ const computedHtmlFor = controlId ?? htmlFor
35
+ return (
36
+ <label ref={ref} htmlFor={computedHtmlFor} className={useClassname("sg-input-group-text", className)} {...restProps}>
37
+ {children}
38
+ </label>
39
+ )
40
+ })
41
+ Text.displayName = "InputGroupText"
42
+
43
+ export default Object.assign(InputGroup, {
44
+ Text: Text,
45
+ Grid: Grid
46
+ })
@@ -0,0 +1,22 @@
1
+ import { ReactNode } from "react";
2
+
3
+ import { BaseDivType, BaseLabelType } from "../utils/BaseTypes";
4
+
5
+ export type InputGroupType = {
6
+ children: ReactNode,
7
+ className?: string,
8
+ controlId: string
9
+ } & BaseDivType
10
+
11
+ export type InputGroupText = {
12
+ children: ReactNode,
13
+ className?: string,
14
+ htmlFor?: string,
15
+ as?: string
16
+ } & BaseLabelType
17
+
18
+ export type InputGroupGridType = {
19
+ children: ReactNode,
20
+ className?:string,
21
+
22
+ } & BaseDivType
@@ -0,0 +1,4 @@
1
+ import InputGroup from "./InputGroup"
2
+ export default InputGroup
3
+ export * from "./InputGroup"
4
+ export type { InputGroupText, InputGroupType} from "./InputGroup.types"