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,527 @@
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
+ const 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 listPositionSetter = (listRef: any) => {
426
+ const listElement = listRef.current
427
+ if(!listElement) return
428
+ const parent = listElement.parentElement
429
+ if(!parent) return
430
+ let position: React.CSSProperties = {}
431
+ const {height: listHeight } = listElement.getBoundingClientRect()
432
+ const { top, bottom} = parent.getBoundingClientRect()
433
+ const {innerHeight} = window
434
+
435
+ const isTop = top > listHeight
436
+ const isBottom = innerHeight - bottom > listHeight
437
+ const coordinate = "calc(100% + 4px)"
438
+ if(isBottom) {
439
+ position = {top: coordinate, bottom: "unset"}
440
+ } else if (isTop) {
441
+ position = {bottom:coordinate, top: "unset"}
442
+ } else {
443
+ const height = innerHeight - bottom - 2
444
+ position = {top: coordinate, maxHeight: height}
445
+ }
446
+ if(!listElement.children) return
447
+ const listChildren = listElement.children
448
+ const numberChildren = listChildren.length
449
+ const numberOfRenderedChildren = numberChildren > 5 ? 5 : numberChildren
450
+ let renderedListHeight = 0
451
+ for (let i=0; i< numberOfRenderedChildren; i++) {
452
+ renderedListHeight += listChildren[i].getBoundingClientRect().height
453
+ }
454
+ renderedListHeight += 4
455
+ position.height = renderedListHeight ?? "auto"
456
+
457
+ return position
458
+ }
459
+
460
+ const SelectList = forwardRef<HTMLUListElement, FormSelectListType>( ({children, className, id, ...restProps}, ref) => {
461
+ const { showList, internalId } = useSelectContext()
462
+
463
+ const [computedStyle, setComputedStyle] = useState<React.CSSProperties>({})
464
+
465
+ const listRef = useRef<HTMLUListElement>(null)
466
+ useLayoutEffect(() => {
467
+ if(!showList) {
468
+ return
469
+ }
470
+ const newPosition = listPositionSetter(listRef)
471
+ setComputedStyle(newPosition!)
472
+ }, [showList, listRef.current])
473
+
474
+ return (
475
+ <ul
476
+ role="listbox" ref={mergeRefs([ref, listRef])} id={internalId+"-list"}
477
+ className={mergeClassnames("sg-select-list", className)} style={showList ? {...computedStyle} : {display:"none"}}
478
+ {...restProps}
479
+ >
480
+ {children}
481
+ </ul>
482
+ )
483
+ })
484
+ SelectList.displayName = "FormSelectList"
485
+
486
+ const SelectOption = forwardRef<HTMLLIElement, FormSelectOptionType>(({
487
+ children, className, id, value, disabled, label, selected,
488
+ onPointerDown, onPointerOver, onClick, ...restProps
489
+ }, ref) => {
490
+ const { internalId, activeDescendant, setActiveDescendant, selectedDescendant, setSelectedDescendant, setShowList } = useSelectContext()
491
+ const handlePointerEnter = (event: React.PointerEvent<HTMLLIElement>) => {
492
+ if(!event.target) return
493
+ setActiveDescendant(value)
494
+ if(onPointerOver) onPointerOver(event)
495
+ }
496
+ const handleCLick = (event: React.MouseEvent<HTMLLIElement>) => {
497
+ event.stopPropagation()
498
+ if(disabled) return
499
+
500
+ setSelectedDescendant( (prev:any) => ({
501
+ value: value,
502
+ label: children
503
+ }))
504
+ setShowList(false)
505
+
506
+ if(onClick) onClick(event)
507
+ }
508
+
509
+ const computedClassName = mergeClassnames("sg-select-list-item", className, activeDescendant === value ? "focus":"")
510
+ return (
511
+ <li role="option" aria-selected={selectedDescendant.value === value ? true:false}
512
+ ref={ref} id={internalId+"-list-item-"+value} className={computedClassName}
513
+ onPointerOver={handlePointerEnter} onClick={handleCLick}
514
+ {...restProps}
515
+ >
516
+ {children}
517
+ </li>
518
+ )
519
+ })
520
+ SelectOption.displayName = "FormSelectOption"
521
+
522
+ export default Object.assign(Select, {
523
+ Control: SelectControl,
524
+ Input: SelectInput,
525
+ Options: SelectList,
526
+ Option: SelectOption
527
+ })
@@ -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"