stargazer-ui 1.5.21 → 1.5.23

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 (377) 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 +16 -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 +163 -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 +441 -0
  38. package/src/Dropdown/Dropdown.types.ts +60 -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 +399 -0
  47. package/src/Form/Form.types.ts +169 -0
  48. package/src/Form/FormSelect.tsx +620 -0
  49. package/src/Form/index.ts +4 -0
  50. package/src/InputGroup/InputGroup.tsx +46 -0
  51. package/src/InputGroup/InputGroup.types.ts +22 -0
  52. package/src/InputGroup/index.ts +4 -0
  53. package/src/List/List.tsx +112 -0
  54. package/src/List/List.types.ts +34 -0
  55. package/src/List/index.ts +4 -0
  56. package/src/Modal/Modal.tsx +229 -0
  57. package/src/Modal/Modal.types.ts +49 -0
  58. package/src/Modal/index.ts +4 -0
  59. package/src/Nav/Nav.tsx +43 -0
  60. package/src/Nav/Nav.types.ts +21 -0
  61. package/src/Nav/index.ts +4 -0
  62. package/src/NavBar/Navbar.tsx +57 -0
  63. package/src/NavBar/Navbar.types.ts +24 -0
  64. package/src/NavBar/index.ts +4 -0
  65. package/src/NavDropdown/NavDropdown.tsx +93 -0
  66. package/src/NavDropdown/NavDropdown.types.ts +7 -0
  67. package/src/NavDropdown/index.ts +3 -0
  68. package/src/Overlay/Overlay.tsx +305 -0
  69. package/src/Overlay/Overlay.types.ts +24 -0
  70. package/{Overlay/index.d.ts → src/Overlay/index.ts} +3 -3
  71. package/src/Popout/Popout.tsx +155 -0
  72. package/src/Popout/Popout.types.ts +42 -0
  73. package/src/Popout/index.ts +3 -0
  74. package/src/Spinner/Spinner.tsx +15 -0
  75. package/src/Spinner/Spinner.types.ts +7 -0
  76. package/src/Spinner/index.ts +3 -0
  77. package/src/Table/Table.tsx +16 -0
  78. package/src/Table/Table.types.ts +9 -0
  79. package/src/Table/index.ts +3 -0
  80. package/src/Tabs/Tabs.tsx +233 -0
  81. package/src/Tabs/Tabs.types.ts +52 -0
  82. package/src/Tabs/index.ts +3 -0
  83. package/src/ToggleButton/ToggleButton.tsx +21 -0
  84. package/src/ToggleButton/ToggleButton.types.ts +8 -0
  85. package/src/ToggleButton/index.ts +3 -0
  86. package/src/assets/tooltip-pointer.svg +3 -0
  87. package/src/assets/warning.svg +39 -0
  88. package/{hooks/index.d.ts → src/hooks/index.ts} +7 -6
  89. package/src/hooks/useClassname.ts +5 -0
  90. package/src/hooks/useDraggable.ts +186 -0
  91. package/src/hooks/useKeepElementFocused.ts +37 -0
  92. package/src/hooks/useQueryParams.ts +12 -0
  93. package/src/hooks/useScreenSize.ts +24 -0
  94. package/src/index.ts +21 -0
  95. package/src/styles/_Card.scss +166 -0
  96. package/src/styles/_CloseButton.scss +51 -0
  97. package/src/styles/_CustomButton.scss +134 -0
  98. package/src/styles/_Dropdown.scss +127 -0
  99. package/src/styles/_FloatingLabel.scss +56 -0
  100. package/src/styles/_Forms.scss +7 -0
  101. package/src/styles/_Grid.scss +178 -0
  102. package/src/styles/_InputGroup.scss +71 -0
  103. package/src/styles/_List.scss +62 -0
  104. package/src/styles/_Modal.scss +234 -0
  105. package/src/styles/_ModalOld.scss +242 -0
  106. package/src/styles/_Nav.scss +36 -0
  107. package/src/styles/_NavBar.scss +116 -0
  108. package/src/styles/_NavDropdown.scss +33 -0
  109. package/src/styles/_OffCanvas.scss +260 -0
  110. package/src/styles/_OverLay.scss +79 -0
  111. package/src/styles/_Popout.scss +75 -0
  112. package/src/styles/_Resizable.scss +64 -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 +41 -0
  118. package/src/styles/_functions.scss +134 -0
  119. package/src/styles/_mixins.scss +26 -0
  120. package/src/styles/_reset.scss +239 -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 +222 -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/utils/createFastContext.tsx +119 -0
  138. package/src/utils/index.ts +4 -0
  139. package/src/vite-env.d.ts +1 -0
  140. package/tsconfig-build.json +4 -0
  141. package/tsconfig.json +79 -0
  142. package/tsconfig.node.json +10 -0
  143. package/types/BaseTypes.d.ts +19 -0
  144. package/{Button → types/components/Button}/Button.types.d.ts +2 -1
  145. package/types/components/Button/index.d.ts +1 -0
  146. package/{Card → types/components/Card}/Card.d.ts +12 -12
  147. package/{Card → types/components/Card}/Card.types.d.ts +1 -3
  148. package/types/components/Card/index.d.ts +1 -0
  149. package/{CloseButton → types/components/CloseButton}/CloseButton.types.d.ts +1 -1
  150. package/types/components/CloseButton/index.d.ts +1 -0
  151. package/types/components/Dropdown/Dropdown.d.ts +71 -0
  152. package/{Dropdown → types/components/Dropdown}/Dropdown.types.d.ts +12 -13
  153. package/types/components/Dropdown/index.d.ts +1 -0
  154. package/{FloatingLabel → types/components/FloatingLabel}/FloatingLabel.types.d.ts +1 -1
  155. package/types/components/FloatingLabel/index.d.ts +1 -0
  156. package/types/components/Form/Form.d.ts +17 -0
  157. package/types/components/Form/Form.types.d.ts +50 -0
  158. package/types/components/Form/index.d.ts +1 -0
  159. package/types/components/InputGroup/InputGroup.d.ts +6 -0
  160. package/types/components/InputGroup/InputGroup.types.d.ts +10 -0
  161. package/types/components/InputGroup/index.d.ts +1 -0
  162. package/{Modal → types/components/Modal}/Modal.d.ts +19 -19
  163. package/{Modal → types/components/Modal}/Modal.types.d.ts +2 -3
  164. package/types/components/Modal/index.d.ts +1 -0
  165. package/{Nav → types/components/Nav}/Nav.d.ts +12 -12
  166. package/{Nav → types/components/Nav}/Nav.types.d.ts +1 -1
  167. package/types/components/Nav/index.d.ts +1 -0
  168. package/{NavBar → types/components/NavBar}/Navbar.d.ts +8 -7
  169. package/{NavBar → types/components/NavBar}/Navbar.types.d.ts +1 -2
  170. package/types/components/NavBar/index.d.ts +1 -0
  171. package/types/components/NavDropdown/NavDropdown.d.ts +35 -0
  172. package/{NavDropdown → types/components/NavDropdown}/NavDropdown.types.d.ts +1 -2
  173. package/types/components/NavDropdown/index.d.ts +1 -0
  174. package/{Popout → types/components/Popout}/Popout.d.ts +6 -6
  175. package/{Popout → types/components/Popout}/Popout.types.d.ts +1 -1
  176. package/types/components/Popout/index.d.ts +1 -0
  177. package/types/components/Spinner/index.d.ts +1 -0
  178. package/{Table → types/components/Table}/Table.types.d.ts +1 -1
  179. package/types/components/Table/index.d.ts +1 -0
  180. package/{Tabs → types/components/Tabs}/Tabs.types.d.ts +2 -12
  181. package/types/components/Tabs/index.d.ts +1 -0
  182. package/types/components/ToggleButton/ToggleButton.d.ts +9 -0
  183. package/types/components/ToggleButton/ToggleButton.types.d.ts +0 -0
  184. package/types/components/ToggleButton/index.d.ts +1 -0
  185. package/types/components/index.d.ts +16 -0
  186. package/types/index.d.ts +1 -0
  187. package/vite.config.js +57 -0
  188. package/vite.config.js.timestamp-1708777378490-e94428ceb2bf9.mjs +42 -0
  189. package/Bar/Bar.type.d.ts +0 -6
  190. package/Bar/index.js +0 -2
  191. package/Bar/index.js.map +0 -1
  192. package/Bar/package.json +0 -1
  193. package/Button/Button.js +0 -15
  194. package/Button/Button.js.map +0 -1
  195. package/Button/index.d.ts +0 -3
  196. package/Button/index.js +0 -7
  197. package/Button/index.js.map +0 -1
  198. package/Button/package.json +0 -1
  199. package/ButtonGroup/ButtonGroup.d.ts +0 -4
  200. package/ButtonGroup/ButtonGroup.js +0 -11
  201. package/ButtonGroup/ButtonGroup.js.map +0 -1
  202. package/ButtonGroup/ButtonGroup.types.d.ts +0 -7
  203. package/ButtonGroup/index.d.ts +0 -3
  204. package/ButtonGroup/index.js +0 -7
  205. package/ButtonGroup/index.js.map +0 -1
  206. package/ButtonGroup/package.json +0 -1
  207. package/Card/Card.js +0 -42
  208. package/Card/Card.js.map +0 -1
  209. package/Card/index.d.ts +0 -3
  210. package/Card/index.js +0 -7
  211. package/Card/index.js.map +0 -1
  212. package/Card/package.json +0 -1
  213. package/CloseButton/CloseButton.js +0 -11
  214. package/CloseButton/CloseButton.js.map +0 -1
  215. package/CloseButton/index.d.ts +0 -3
  216. package/CloseButton/index.js +0 -7
  217. package/CloseButton/index.js.map +0 -1
  218. package/CloseButton/package.json +0 -1
  219. package/Dropdown/Dropdown.d.ts +0 -39
  220. package/Dropdown/Dropdown.js +0 -380
  221. package/Dropdown/Dropdown.js.map +0 -1
  222. package/Dropdown/index.d.ts +0 -4
  223. package/Dropdown/index.js +0 -8
  224. package/Dropdown/index.js.map +0 -1
  225. package/Dropdown/package.json +0 -1
  226. package/FileUploadButton/FileUploadButton.d.ts +0 -4
  227. package/FileUploadButton/FileUploadButton.js +0 -20
  228. package/FileUploadButton/FileUploadButton.js.map +0 -1
  229. package/FileUploadButton/FileUploadButton.types.d.ts +0 -7
  230. package/FileUploadButton/index.d.ts +0 -3
  231. package/FileUploadButton/index.js +0 -7
  232. package/FileUploadButton/index.js.map +0 -1
  233. package/FileUploadButton/package.json +0 -1
  234. package/FloatingLabel/FloatingLabel.js +0 -15
  235. package/FloatingLabel/FloatingLabel.js.map +0 -1
  236. package/FloatingLabel/index.d.ts +0 -3
  237. package/FloatingLabel/index.js +0 -7
  238. package/FloatingLabel/index.js.map +0 -1
  239. package/FloatingLabel/package.json +0 -1
  240. package/Form/Form.d.ts +0 -37
  241. package/Form/Form.js +0 -227
  242. package/Form/Form.js.map +0 -1
  243. package/Form/Form.types.d.ts +0 -159
  244. package/Form/FormSelect.d.ts +0 -12
  245. package/Form/FormSelect.js +0 -492
  246. package/Form/FormSelect.js.map +0 -1
  247. package/Form/index.d.ts +0 -4
  248. package/Form/index.js +0 -8
  249. package/Form/index.js.map +0 -1
  250. package/Form/package.json +0 -1
  251. package/InputGroup/InputGroup.d.ts +0 -7
  252. package/InputGroup/InputGroup.js +0 -31
  253. package/InputGroup/InputGroup.js.map +0 -1
  254. package/InputGroup/InputGroup.types.d.ts +0 -17
  255. package/InputGroup/index.d.ts +0 -4
  256. package/InputGroup/index.js +0 -7
  257. package/InputGroup/index.js.map +0 -1
  258. package/InputGroup/package.json +0 -1
  259. package/List/List.d.ts +0 -14
  260. package/List/List.js +0 -77
  261. package/List/List.js.map +0 -1
  262. package/List/List.types.d.ts +0 -28
  263. package/List/index.d.ts +0 -3
  264. package/List/index.js +0 -7
  265. package/List/index.js.map +0 -1
  266. package/List/package.json +0 -1
  267. package/Modal/Modal.js +0 -157
  268. package/Modal/Modal.js.map +0 -1
  269. package/Modal/index.d.ts +0 -3
  270. package/Modal/index.js +0 -7
  271. package/Modal/index.js.map +0 -1
  272. package/Modal/package.json +0 -1
  273. package/Nav/Nav.js +0 -29
  274. package/Nav/Nav.js.map +0 -1
  275. package/Nav/index.d.ts +0 -4
  276. package/Nav/index.js +0 -7
  277. package/Nav/index.js.map +0 -1
  278. package/Nav/package.json +0 -1
  279. package/NavBar/Navbar.js +0 -36
  280. package/NavBar/Navbar.js.map +0 -1
  281. package/NavBar/index.d.ts +0 -4
  282. package/NavBar/index.js +0 -8
  283. package/NavBar/index.js.map +0 -1
  284. package/NavBar/package.json +0 -1
  285. package/NavDropdown/NavDropdown.d.ts +0 -19
  286. package/NavDropdown/NavDropdown.js +0 -75
  287. package/NavDropdown/NavDropdown.js.map +0 -1
  288. package/NavDropdown/index.d.ts +0 -3
  289. package/NavDropdown/index.js +0 -7
  290. package/NavDropdown/index.js.map +0 -1
  291. package/NavDropdown/package.json +0 -1
  292. package/Overlay/Overlay.d.ts +0 -4
  293. package/Overlay/Overlay.js +0 -241
  294. package/Overlay/Overlay.js.map +0 -1
  295. package/Overlay/Overlay.types.d.ts +0 -22
  296. package/Overlay/index.js +0 -7
  297. package/Overlay/index.js.map +0 -1
  298. package/Overlay/package.json +0 -1
  299. package/Popout/Popout.js +0 -111
  300. package/Popout/Popout.js.map +0 -1
  301. package/Popout/index.d.ts +0 -3
  302. package/Popout/index.js +0 -7
  303. package/Popout/index.js.map +0 -1
  304. package/Popout/package.json +0 -1
  305. package/Spinner/Spinner.js +0 -11
  306. package/Spinner/Spinner.js.map +0 -1
  307. package/Spinner/index.d.ts +0 -3
  308. package/Spinner/index.js +0 -7
  309. package/Spinner/index.js.map +0 -1
  310. package/Spinner/package.json +0 -1
  311. package/Table/Table.js +0 -12
  312. package/Table/Table.js.map +0 -1
  313. package/Table/index.d.ts +0 -3
  314. package/Table/index.js +0 -7
  315. package/Table/index.js.map +0 -1
  316. package/Table/package.json +0 -1
  317. package/Tabs/Tabs.js +0 -162
  318. package/Tabs/Tabs.js.map +0 -1
  319. package/Tabs/index.d.ts +0 -3
  320. package/Tabs/index.js +0 -7
  321. package/Tabs/index.js.map +0 -1
  322. package/Tabs/package.json +0 -1
  323. package/ToggleButton/ToggleButton.d.ts +0 -4
  324. package/ToggleButton/ToggleButton.js +0 -18
  325. package/ToggleButton/ToggleButton.js.map +0 -1
  326. package/ToggleButton/ToggleButton.types.d.ts +0 -7
  327. package/ToggleButton/index.d.ts +0 -3
  328. package/ToggleButton/index.js +0 -7
  329. package/ToggleButton/index.js.map +0 -1
  330. package/ToggleButton/package.json +0 -1
  331. package/hooks/index.js +0 -7
  332. package/hooks/index.js.map +0 -1
  333. package/hooks/package.json +0 -1
  334. package/hooks/useClassname.d.ts +0 -2
  335. package/hooks/useClassname.js +0 -7
  336. package/hooks/useClassname.js.map +0 -1
  337. package/hooks/useDraggable.d.ts +0 -23
  338. package/hooks/useDraggable.js +0 -147
  339. package/hooks/useDraggable.js.map +0 -1
  340. package/hooks/useKeepElementFocused.d.ts +0 -2
  341. package/hooks/useKeepElementFocused.js +0 -37
  342. package/hooks/useKeepElementFocused.js.map +0 -1
  343. package/hooks/useQueryParams.d.ts +0 -2
  344. package/hooks/useQueryParams.js +0 -13
  345. package/hooks/useQueryParams.js.map +0 -1
  346. package/hooks/useScreenSize.d.ts +0 -5
  347. package/hooks/useScreenSize.js +0 -21
  348. package/hooks/useScreenSize.js.map +0 -1
  349. package/index.d.ts +0 -19
  350. package/index.js +0 -20
  351. package/index.js.map +0 -1
  352. package/styles/stargazerui.css +0 -6658
  353. package/styles/stargazerui.css.map +0 -1
  354. package/utils/ContrastingColor.d.ts +0 -1
  355. package/utils/CreateSyntheticEvent.d.ts +0 -2
  356. package/utils/CreateSyntheticEvent.js +0 -33
  357. package/utils/CreateSyntheticEvent.js.map +0 -1
  358. package/utils/IsInputKey.d.ts +0 -7
  359. package/utils/IsInputKey.js +0 -20
  360. package/utils/IsInputKey.js.map +0 -1
  361. package/utils/MergeClassnames.d.ts +0 -2
  362. package/utils/MergeClassnames.js +0 -7
  363. package/utils/MergeClassnames.js.map +0 -1
  364. package/utils/MergeRefs.d.ts +0 -1
  365. package/utils/MergeRefs.js +0 -16
  366. package/utils/MergeRefs.js.map +0 -1
  367. package/utils/createFastContext.d.ts +0 -16
  368. package/utils/createFastContext.js +0 -101
  369. package/utils/createFastContext.js.map +0 -1
  370. package/utils/index.d.ts +0 -4
  371. /package/{Button → types/components/Button}/Button.d.ts +0 -0
  372. /package/{CloseButton → types/components/CloseButton}/CloseButton.d.ts +0 -0
  373. /package/{FloatingLabel → types/components/FloatingLabel}/FloatingLabel.d.ts +0 -0
  374. /package/{Spinner → types/components/Spinner}/Spinner.d.ts +0 -0
  375. /package/{Spinner → types/components/Spinner}/Spinner.types.d.ts +0 -0
  376. /package/{Table → types/components/Table}/Table.d.ts +0 -0
  377. /package/{Tabs → types/components/Tabs}/Tabs.d.ts +0 -0
@@ -0,0 +1,620 @@
1
+ import { forwardRef, useState, useEffect, useLayoutEffect, useRef, useMemo, createContext, useContext, useCallback } from "react"
2
+ import { createPortal } from "react-dom"
3
+ import { FormSelectType, FormSelectControlType, FormSelectInputType, FormSelectListType, FormSelectOptionType, SelectContextType } from "./Form.types"
4
+
5
+ import Overlay from "../Overlay"
6
+
7
+ import { useFormContext, useFormTagContext, ErrorMessage, HintMessage } from "./Form"
8
+ import mergeRefs from "../utils/MergeRefs"
9
+ import mergeClassnames from "../utils/MergeClassnames"
10
+ import createSyntheticEvent from "../utils/CreateSyntheticEvent"
11
+ import { InputKeyType, isValidInputKey } from "../utils/IsInputKey"
12
+ import createFastContext from "../utils/createFastContext"
13
+
14
+ /*
15
+ export const SelectContext = createContext<SelectContextType | null>(null)
16
+ export const SelectContextProvider = ({children, value} : {children: React.ReactNode, value:SelectContextType}) => {
17
+ return (
18
+ <SelectContext.Provider value={value}>
19
+ {children}
20
+ </SelectContext.Provider>
21
+ )
22
+ }
23
+ export const useSelectContext = () => {
24
+ const context = useContext(SelectContext)
25
+ if(!context) {
26
+ throw new Error("UseSelectContext must be used within a SelectContextProvider!")
27
+ }
28
+ return context
29
+ }
30
+ */
31
+
32
+ const {Provider: SelectContextProvider, setStore, getStore: getSelectContext, checkContext} = createFastContext<SelectContextType<{label:any, value:any}>>()
33
+
34
+ type CustomStateMiddleware<T> = (newValue: T) => void
35
+ type CustomSetStateSelector<T> = (oldValue: T) => T | T
36
+ type CustomSetStateOptions = { middleware: true }
37
+ export type CustomSetState<T> = (callback: CustomSetStateSelector<T>, options?: CustomSetStateOptions) => void
38
+ type CustomStateReturn<T> = [T, CustomSetState<T>]
39
+ const useCustomState = <T, >(initialValue: T, middleware?: CustomStateMiddleware<T>) : CustomStateReturn<T> => {
40
+ const [state, setState] = useState(initialValue)
41
+ const customSetState = (callback: CustomSetStateSelector<T>, options: CustomSetStateOptions = { middleware:true }) => {
42
+ let newValue: T
43
+ if( typeof callback === "function") {
44
+ newValue = callback(state)
45
+ } else {
46
+ newValue = callback
47
+ }
48
+ if(middleware && options.middleware === true) middleware(newValue)
49
+ setState(newValue)
50
+ }
51
+ return [state, customSetState]
52
+ }
53
+
54
+ const inputKeys: InputKeyType[] = [
55
+ {
56
+ id: "ArrowDown",
57
+ alt: true
58
+ },
59
+ {
60
+ id: "ArrowUp",
61
+ alt: true
62
+ },
63
+ { id: "Home" }, { id: "End" }, { id: "Enter" }, { id: "Escape" },
64
+ { id: "PageDown" }, { id: "PageUp" }, { id: " " }, { id: "Tab" }
65
+ ]
66
+
67
+
68
+ const Select = forwardRef<HTMLButtonElement, FormSelectType>( ({
69
+ children, className, id, required=false, disabled=false, value, defaultValue, label,
70
+ errorAsOverlay, error, hint, "aria-describedby":ariaDescribedby, loading=false, listProps,
71
+ onChange,
72
+ ...restProps
73
+ }, ref) => {
74
+
75
+ if(Array.isArray(children)) {
76
+ children = children.flat(Infinity).filter(child => child !== null && child !== undefined)
77
+ } else {
78
+ children = [children].filter(child => child !== null && child !== undefined)
79
+ }
80
+
81
+ const internalButtonRef = useRef<HTMLButtonElement>(null)
82
+ const { noValidate } = useFormTagContext()
83
+ const { controlId, isInputGroup, isFLoatingLabel } = useFormContext()
84
+
85
+ const isOverlay = isInputGroup || isFLoatingLabel || errorAsOverlay
86
+ const computedClassName = mergeClassnames(
87
+ "sg-form-select", className, error ? "invalid":"", disabled ? "disabled":""
88
+ )
89
+
90
+ const elementId = controlId ?? id
91
+ if(elementId === undefined) {
92
+ throw new Error(
93
+ "Form.Select needs to have an id, either provided directly through the 'id' property or wrapped in a Form.Group with a 'controlId' !"
94
+ )
95
+ }
96
+
97
+ const hasValidChildren = useMemo(() => {
98
+ if(!children || !Array.isArray(children)) return false
99
+ if(children.length < 1) return false
100
+
101
+ let isValid = true
102
+ children.forEach(child => {
103
+ if(child.props.value === undefined || child.props.value === null) {
104
+ isValid = false
105
+ }
106
+ })
107
+
108
+ if(children[0].props.value && children[0].props.value != "") {
109
+ //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")
110
+ }
111
+ return isValid
112
+ }, [children])
113
+ if(!hasValidChildren || !Array.isArray(children)) {
114
+ throw new Error(
115
+ "Form.Select needs to have 1 or more 'Form.Select.Option' children, each with value attributes!"
116
+ )
117
+ }
118
+
119
+ const errorMessageId = error ? elementId+"-error-message":undefined
120
+ const hintMessageId = hint ? elementId+"-hint-message":undefined
121
+ const tooltipMessage = isOverlay && (error || hint) ?
122
+ <div className="sg-form-control-description tooltip">
123
+ {error? <ErrorMessage id={errorMessageId} message={error.message} /> : null}
124
+ {hint? <HintMessage id={hintMessageId} message={hint.message} /> : null}
125
+ </div> : undefined// "Testing a tooltip with a long message. This messsage is so long it hsould in theory trigger a wrap."
126
+
127
+ const describedby = mergeClassnames(ariaDescribedby, errorMessageId, hintMessageId)
128
+
129
+ const valueChild = children.find(child => child.props.value === value)
130
+ const defaultValueChild = children.find(child => child.props.value === defaultValue)
131
+ const initialValue = value != undefined && value != null ?
132
+ {
133
+ value: value,
134
+ label: valueChild ? valueChild.props.children : "no label found"
135
+ }:
136
+ defaultValue != undefined && defaultValue != null ?
137
+ {
138
+ value: defaultValueChild ? defaultValueChild.props.value : "",
139
+ label: defaultValueChild ? defaultValueChild.props.children : "no label found"
140
+ }
141
+ :
142
+ {
143
+ value: children[0].props.value ?? "",
144
+ label: children[0].props.children ?? "no label found"
145
+ }
146
+ const handleChange = (newValue: any) => {
147
+ if(!internalButtonRef.current) return
148
+
149
+ const target = internalButtonRef.current
150
+ target.value = newValue.value;
151
+ const event = new Event('change', { bubbles: true });
152
+ Object.defineProperty(event, 'target', { writable: false, value: target })
153
+ const syntheticEvent = createSyntheticEvent(event) as React.ChangeEvent<typeof target>;
154
+ if(onChange) onChange(syntheticEvent);
155
+ }
156
+ const [ selectedDescendant, setSelectedDescendant ] = useState<{value:any, label:any}>(initialValue)// useCustomState<{value:any, label:any}>(initialValue, handleChange)
157
+ const handleSelectedDescendant = (newDescendant: typeof selectedDescendant) => {
158
+ setSelectedDescendant(newDescendant)
159
+ if(onChange) handleChange(newDescendant)
160
+ }
161
+ /*
162
+ if(value != undefined && value != null && value !== selectedDescendant.value && !loading) {
163
+ const isChild = children.find(child => child.props.value === value)
164
+ const isDefaultChild = children.find(child => child.props.value === defaultValue)
165
+ const computedLabel = isChild ? isChild.props.children : (isDefaultChild ? isDefaultChild.props.children : "no label found")
166
+ const computedValue = isChild ? value : (isDefaultChild ? defaultValue : "")
167
+ setSelectedDescendant({
168
+ value: computedValue,
169
+ label: computedLabel
170
+ }, {middleware: false})
171
+ }
172
+ */
173
+
174
+ const context = {
175
+ internalButtonRef: internalButtonRef,
176
+ internalId: elementId,
177
+ showList: false,
178
+ activeDescendant: initialValue.value,
179
+ inputValue: "",
180
+ selectedDescendant,
181
+ setSelectedDescendant: handleSelectedDescendant,
182
+ items: children
183
+ }
184
+
185
+ return (
186
+ <SelectContextProvider initialState={context} refreshKeys={["items", "internalId", "setSelectedDescendant"]}>
187
+ <Overlay trigger={"focus"} position="bottom" tooltip={tooltipMessage}>
188
+ <SelectControl
189
+ value={selectedDescendant.value} label={selectedDescendant.label} handleSelectedDescendant={handleSelectedDescendant}
190
+ className={computedClassName} id={elementId} required={(required && !noValidate) ?? undefined} disabled={disabled}
191
+ aria-required={required ?? undefined} aria-invalid={error ? "true":"false"} aria-describedby={describedby != "" ? describedby : undefined}
192
+ {...restProps} ref={mergeRefs([ref, internalButtonRef])}
193
+ >
194
+ <SelectList>
195
+ {!loading ?
196
+ children
197
+ :
198
+ <SelectOption disabled={true} key="loading" value="">
199
+ Loading...
200
+ </SelectOption>
201
+ }
202
+ </SelectList>
203
+ </SelectControl>
204
+ </Overlay>
205
+ <div className="sg-form-control-description">
206
+ {error && !isOverlay ?
207
+ <ErrorMessage id={elementId} message={error.message} style={error.style} className={error.className}/>
208
+ : null }
209
+ {hint && !isOverlay ?
210
+ <HintMessage id={elementId} message={hint.message} style={hint.style} className={hint.className}/>
211
+ : null }
212
+ </div>
213
+ </SelectContextProvider>
214
+ )
215
+ })
216
+ Select.displayName = "FormSelect"
217
+ /*
218
+ const SelectInput = forwardRef<HTMLInputElement, FormSelectInputType>( ({className, id, value, onChange, ...restProps}, ref ) => {
219
+ const { showList, setShowList, inputValue, setInputValue, internalId } = useSelectContext()
220
+
221
+ const debouncedInput = (value: string) => {
222
+ setTimeout(()=>{
223
+ setInputValue(value)
224
+ }, 300)
225
+ }
226
+ const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
227
+ if(!showList) { setShowList(true) }
228
+ if(onChange) { onChange(event) }
229
+ //debouncedInput(event.target.value)
230
+ setInputValue(event.target.value)
231
+ }
232
+ const handleKeyUp = (event: React.KeyboardEvent) => {
233
+ const key = event.key
234
+ switch(key) {
235
+ case "ArrowDown":
236
+ console.log("down arrow")
237
+ break
238
+ case "ArrowUp":
239
+ console.log("up arrow")
240
+ break
241
+ }
242
+ }
243
+
244
+
245
+ return (
246
+ <input value={inputValue} ref={ref}
247
+ onKeyUp={handleKeyUp} onChange={handleChange}
248
+ className={mergeClassnames("sg-select-input", className)}
249
+ {...restProps}
250
+ />
251
+ )
252
+ })
253
+ SelectInput.displayName = "FormSelectInput"
254
+ */
255
+ const SelectControl = forwardRef<HTMLButtonElement, FormSelectControlType>( ({
256
+ children, className, value, label, searchable=false, required=false,
257
+ onClick, onBlur, onKeyUp, onKeyDown, handleSelectedDescendant,
258
+ ...restProps
259
+ }, ref) => {
260
+ const setSelectContext = setStore()
261
+ const [ activeDescendant, selectedDescendant, showList, internalId, items ] = getSelectContext(state => [ state.activeDescendant, state.selectedDescendant, state.showList, state.internalId, state.items ])
262
+ const internalButtonRef = useRef<HTMLButtonElement>(null)
263
+ const isVisible = useRef(true)
264
+ const [rootMargin, setRootMargin] = useState("0px 0px 0px 0px")
265
+ useEffect(() => {
266
+ if(selectedDescendant.value != value) {
267
+ setSelectContext({selectedDescendant:{value, label}})
268
+ }
269
+ }, [value])
270
+ const computedClassName = mergeClassnames(className, "sg-select-control")
271
+
272
+ const resetFocus = () => {
273
+ setSelectContext({activeDescendant: selectedDescendant.value})
274
+ }
275
+ const handleSetShowList = (show: boolean | ((show:boolean) => boolean)) => {
276
+ const newShow = typeof show === "boolean" ? show : show(showList)
277
+ if(!newShow) {
278
+ resetFocus()
279
+ } else {
280
+ const button = internalButtonRef.current
281
+ if(!button || !button.parentElement) return
282
+ //const isScrolling = button.parentElement.scrollHeight > button.parentElement.clientHeight
283
+ button.click()
284
+
285
+ if(!isVisible.current) {
286
+ button.scrollIntoView()
287
+ }
288
+ }
289
+ setSelectContext({showList: newShow})
290
+ }
291
+
292
+ const handleClick = (event?: React.MouseEvent<HTMLButtonElement>) => {
293
+ handleSetShowList(prev => !prev)
294
+ if(onClick && event) onClick(event)
295
+ }
296
+
297
+ useEffect(() => {
298
+ const controller = new AbortController()
299
+ const signal = controller.signal
300
+ const select = internalButtonRef.current
301
+ if(!select) return
302
+ const { top, bottom, left, right, width, height } = select.getBoundingClientRect()
303
+ //console.log(select.getBoundingClientRect(), top, left, bottom, right)
304
+
305
+ const intersectionObserver = new IntersectionObserver((entries, observer) => {
306
+ entries.forEach(entry => {
307
+ const target = entry.target
308
+ isVisible.current = entry.isIntersecting
309
+
310
+ if(!entry.isIntersecting) handleSetShowList(false)
311
+ if(target.id != "edit-background-complexity") return
312
+ //console.log(entry)
313
+ /*
314
+ console.lo
315
+ console.log(entry)
316
+ if(entry.intersectionRatio > 0) {
317
+ const testBox = constructBoxWindow( entry.intersectionRect, entry.rootBounds as DOMRect)
318
+ const testMargin = getMargins(testBox, entry.rootBounds)
319
+ setRootMargin(testMargin)
320
+ }
321
+ */
322
+ })
323
+ }, {threshold:1})
324
+ intersectionObserver.observe(select)
325
+ window.addEventListener("pointerdown", event => {
326
+ if(!select.contains(event.target as HTMLElement)) {handleSetShowList(false)}
327
+ }, {signal, capture: true})
328
+ window.addEventListener("resize", event => handleSetShowList(false), {signal, capture: true})
329
+ return function cleanup () {
330
+ controller.abort()
331
+ intersectionObserver.unobserve(select)
332
+ }
333
+ }, [rootMargin])
334
+
335
+ const changeActiveDescendant = (number: number, type: string) => {
336
+ const maxIndex = (items as any).length - 1
337
+ const minIndex = 0
338
+ const currentIndex = (items as any).indexOf((items as any).find((child:any) => child.props.value === activeDescendant))
339
+ let newIndex, temp
340
+ switch (type) {
341
+ case "add":
342
+ temp = currentIndex + number
343
+ newIndex = temp > maxIndex ? maxIndex : temp
344
+ break
345
+ case "sub":
346
+ temp = currentIndex - number
347
+ newIndex = temp < minIndex ? minIndex : temp
348
+ break
349
+ case "set":
350
+ newIndex = number > maxIndex ? maxIndex : (number < minIndex ? 0 : number)
351
+ break
352
+ default:
353
+ newIndex = 0
354
+ break
355
+ }
356
+ setSelectContext({activeDescendant: (items as any)[newIndex].props.value})
357
+ }
358
+ const handleKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {
359
+ const isInputKey = isValidInputKey(event, inputKeys)
360
+ if(onKeyDown) onKeyDown(event)
361
+ if(!isInputKey) return
362
+
363
+ let isPreventDefault = true
364
+ let isStopPropagation = true
365
+
366
+ switch (event.key) {
367
+ case "ArrowDown":
368
+ if(showList) changeActiveDescendant(1, "add")
369
+ break
370
+ case "ArrowUp":
371
+ if(showList) changeActiveDescendant(1, "sub")
372
+ break
373
+ case "Tab":
374
+ if(!showList) {
375
+ isPreventDefault = false
376
+ isStopPropagation = false
377
+ }
378
+ }
379
+ if(isPreventDefault) event.preventDefault()
380
+ if(isStopPropagation) event.stopPropagation()
381
+ }
382
+ const handleKeyUp = (event: React.KeyboardEvent<HTMLButtonElement>) => {
383
+ const isInputKey = isValidInputKey(event, inputKeys)
384
+ if(onKeyUp) onKeyUp(event)
385
+ if(!isInputKey) return
386
+
387
+ let isPreventDefault = true
388
+ let isStopPropagation = true
389
+ switch(event.key) {
390
+ case "ArrowDown":
391
+ if(!showList) {
392
+ handleSetShowList(true)
393
+ }
394
+ break
395
+ case "ArrowUp":
396
+ if(!showList) {
397
+ handleSetShowList(true)
398
+ changeActiveDescendant(0, "set")
399
+ }
400
+ break
401
+ case "Home":
402
+ if(!showList) {
403
+ handleSetShowList(true)
404
+ }
405
+ changeActiveDescendant(0, "set")
406
+ break
407
+ case "End":
408
+ if(!showList) {
409
+ handleSetShowList(true)
410
+ }
411
+ changeActiveDescendant((items as any).length - 1, "set")
412
+ break
413
+ case "Enter":
414
+ case " ":
415
+ handleClick()
416
+ if(showList) {
417
+ const focusElement = document.getElementById(internalId+"-list-item-"+activeDescendant)
418
+ focusElement?.click()
419
+ }
420
+ break
421
+ case "Escape":
422
+ if(showList) {
423
+ handleSetShowList(false)
424
+ }
425
+ break
426
+ case "PageDown":
427
+ changeActiveDescendant(10, "add")
428
+ break
429
+ case "PageUp":
430
+ changeActiveDescendant(10, "sub")
431
+ break
432
+ case "Tab":
433
+ if(showList) {
434
+ handleClick()
435
+ const focusElement = document.getElementById(internalId+"-list-item-"+activeDescendant)
436
+ focusElement?.click()
437
+ }
438
+ isPreventDefault = false
439
+ isStopPropagation = false
440
+ break
441
+ }
442
+ if(isPreventDefault) event.preventDefault()
443
+ if(isStopPropagation) event.stopPropagation()
444
+ }
445
+
446
+ return (
447
+ <button
448
+ role="combobox" aria-controls={internalId+"list"} aria-expanded={showList} aria-activedescendant={activeDescendant === false ? "":internalId+"-list-item-"+activeDescendant}
449
+ ref={mergeRefs([ref, internalButtonRef])} value={value} type="button"
450
+ className={computedClassName} id={internalId+"-control"} title={typeof label === "string" ? label : undefined}
451
+ onMouseUp={handleClick} onKeyDown={handleKeyDown} onKeyUp={handleKeyUp}
452
+ {...restProps}
453
+ >
454
+ <span>{label}</span>
455
+ {children}
456
+ </button>
457
+ )
458
+ })
459
+ SelectControl.displayName = "FormSelectControl"
460
+
461
+ const listPositionSetter = (listRef: any, internalId: string) => {
462
+ const listElement = listRef.current
463
+ if(!listElement) return
464
+ const parent = document.getElementById(internalId)//listElement.parentElement
465
+ if(!parent) return
466
+ let position: React.CSSProperties = {}
467
+ const { height: listHeight } = listElement.getBoundingClientRect()
468
+ const { top, bottom, left, width: parentWidth, height: parentHeight } = parent.getBoundingClientRect()
469
+ const { innerHeight } = window
470
+ if(!listElement.children) return
471
+ const listChildren = listElement.children
472
+ const numberChildren = listChildren.length
473
+ const numberOfRenderedChildren = numberChildren > 5 ? 5 : numberChildren
474
+ let renderedListHeight = 0
475
+ for (let i=0; i< numberOfRenderedChildren; i++) {
476
+ renderedListHeight += listChildren[i].getBoundingClientRect().height
477
+ }
478
+ renderedListHeight += 4
479
+
480
+ const isTop = top > renderedListHeight
481
+ const isBottom = innerHeight - bottom > renderedListHeight
482
+
483
+ const borderWidth = parseFloat(getComputedStyle(parent).borderBottomWidth)
484
+ const coordinate = (isBottom ? top + parentHeight + borderWidth : top - borderWidth) //`calc(100% + ${borderWidth})`
485
+ if(isBottom) {
486
+ position = {top: coordinate, bottom: "unset"}
487
+ } else if (isTop) {
488
+ position = {bottom: coordinate, top: "unset"}
489
+ } else {
490
+ const height = innerHeight - bottom - 2
491
+ position = {top: coordinate, maxHeight: height}
492
+ }
493
+
494
+ position.height = renderedListHeight ?? "auto"
495
+ position.width = parentWidth
496
+ position.left = left
497
+ return position
498
+ }
499
+
500
+ const SelectList = forwardRef<HTMLUListElement, FormSelectListType>( ({children, className, id, ...restProps}, ref) => {
501
+ //const { showList, internalId } = getSelectContext()
502
+ const [showList, internalId, selectedDescendant, internalButtonRef] = getSelectContext(state => [state.showList, state.internalId, state.selectedDescendant, state.internalButtonRef])
503
+ const [computedStyle, setComputedStyle] = useState<React.CSSProperties>({})
504
+
505
+ const listRef = useRef<HTMLUListElement>(null)
506
+ useLayoutEffect(() => {
507
+ if(!showList || !listRef.current) {
508
+ return
509
+ }
510
+ const newPosition = listPositionSetter(listRef, internalId as string)
511
+ setComputedStyle(newPosition!)
512
+
513
+ }, [showList])
514
+ useEffect(() => {
515
+ const controller = new AbortController()
516
+ const signal = controller.signal
517
+ window.addEventListener("scroll", event => {
518
+ if(!showList || !listRef.current) {
519
+ return
520
+ }
521
+ const newPosition = listPositionSetter(listRef, internalId as string)
522
+ setComputedStyle(newPosition!)
523
+ }, {signal, capture:true})
524
+ return function cleanup() {
525
+ controller.abort()
526
+ }
527
+ }, [showList])
528
+ useEffect(() => {
529
+ if(!showList || !listRef.current) {
530
+ return
531
+ }
532
+ const resizeObserver = new ResizeObserver((entries) => {
533
+ for(const entry of entries) {
534
+ if(entry.target.scrollHeight > entry.target.clientHeight) {
535
+ //console.log(entry.target.scrollHeight, entry.target.clientHeight)
536
+ const selectedChild = Array.from(entry.target.children).find(child => child.classList.contains("selected"))
537
+ if(selectedChild) selectedChild.scrollIntoView()
538
+ }
539
+ }
540
+ })
541
+ resizeObserver.observe(listRef.current)
542
+
543
+ return function cleanup () {
544
+ if(!showList || !listRef.current) {
545
+ return
546
+ }
547
+ resizeObserver.unobserve(listRef.current)
548
+ }
549
+ }, [showList])
550
+
551
+ return (
552
+ createPortal(
553
+ <ul
554
+ role="listbox" ref={mergeRefs([ref, listRef])} id={internalId+"-list"}
555
+ className={mergeClassnames("sg-select-list", className)} style={showList ? {...computedStyle} : {display:"none"}}
556
+ {...restProps}
557
+ >
558
+ {children}
559
+ </ul>
560
+ , document.body)
561
+ )
562
+ })
563
+ SelectList.displayName = "FormSelectList"
564
+
565
+ const SelectOption = forwardRef<HTMLLIElement, FormSelectOptionType>(({
566
+ children, className, id, value, disabled, label, selected,
567
+ onPointerDown, onPointerOver, onClick, ...restProps
568
+ }, ref) => {
569
+ const setSelectContext = setStore()
570
+ const isActiveDescendant = getSelectContext(state => state.activeDescendant === value)
571
+ //if(isActiveDescendant) console.log(value)
572
+ const isSelectedDescendant = getSelectContext(state => state.selectedDescendant.value === value)
573
+ const { internalId, setSelectedDescendant } = getSelectContext(state => state, (oldValue, newValue) => {return oldValue.selectedDescendant.value === newValue.selectedDescendant.value})
574
+
575
+ const handlePointerEnter = (event: React.PointerEvent<HTMLLIElement>) => {
576
+ if(!event.target) return
577
+ setSelectContext!({activeDescendant: value})
578
+ if(onPointerOver) onPointerOver(event)
579
+ }
580
+ const handleCLick = (event: React.MouseEvent<HTMLLIElement>) => {
581
+ event.stopPropagation()
582
+ if(disabled) return
583
+
584
+ setSelectedDescendant({
585
+ value: value,
586
+ label: children
587
+ })
588
+ setSelectContext!({showList: false})
589
+
590
+ if(onClick) onClick(event)
591
+ }
592
+
593
+ const computedClassName = mergeClassnames("sg-select-list-item", className, isSelectedDescendant ? "selected":"", isActiveDescendant ? "focus":"")
594
+ return (
595
+ <li role="option" aria-selected={isSelectedDescendant}
596
+ ref={ref} id={internalId+"-list-item-"+value} className={computedClassName}
597
+ onPointerOver={handlePointerEnter} onMouseDown={handleCLick}
598
+ {...restProps}
599
+ >
600
+ {children}
601
+ {isSelectedDescendant ?
602
+ <div className="sg-form-select-check-icon-wrapper" aria-hidden>
603
+ <svg className="sg-form-select-check-icon" xmlns='http://www.w3.org/2000/svg' width="0.75rem" height="0.75rem" viewBox='0 0 32 32'>
604
+ <polygon fill="currentColor"
605
+ points='11.941,28.877 0,16.935 5.695,11.24 11.941,17.486 26.305,3.123 32,8.818'
606
+ />
607
+ </svg>
608
+ </div>
609
+ :null}
610
+ </li>
611
+ )
612
+ })
613
+ SelectOption.displayName = "FormSelectOption"
614
+
615
+ export default Object.assign(Select, {
616
+ Control: SelectControl,
617
+ /*Input: SelectInput,*/
618
+ Options: SelectList,
619
+ Option: SelectOption
620
+ })
@@ -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