fluent-styles 1.53.0 → 1.55.0

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 (872) hide show
  1. package/README.md +4065 -77
  2. package/lib/commonjs/actionSheet/actionSheet.js +372 -0
  3. package/lib/commonjs/actionSheet/actionSheet.js.map +1 -0
  4. package/lib/commonjs/actionSheet/index.js +20 -0
  5. package/lib/commonjs/actionSheet/index.js.map +1 -0
  6. package/lib/commonjs/actionSheet/useActionSheet.js +89 -0
  7. package/lib/commonjs/actionSheet/useActionSheet.js.map +1 -0
  8. package/lib/commonjs/badge/index.js +148 -0
  9. package/lib/commonjs/badge/index.js.map +1 -0
  10. package/lib/commonjs/barChart/index.js +298 -0
  11. package/lib/commonjs/barChart/index.js.map +1 -0
  12. package/lib/commonjs/button/index.js +228 -0
  13. package/lib/commonjs/button/index.js.map +1 -0
  14. package/lib/commonjs/card/index.js +202 -0
  15. package/lib/commonjs/card/index.js.map +1 -0
  16. package/lib/commonjs/checkBox/index.js +90 -0
  17. package/lib/commonjs/checkBox/index.js.map +1 -0
  18. package/lib/commonjs/chips/index.js +239 -0
  19. package/lib/commonjs/chips/index.js.map +1 -0
  20. package/lib/commonjs/circularProgress/index.js +265 -0
  21. package/lib/commonjs/circularProgress/index.js.map +1 -0
  22. package/lib/commonjs/collapsible/Collapse.js +293 -0
  23. package/lib/commonjs/collapsible/Collapse.js.map +1 -0
  24. package/lib/commonjs/collapsible/CollapseGroup.js +137 -0
  25. package/lib/commonjs/collapsible/CollapseGroup.js.map +1 -0
  26. package/lib/commonjs/collapsible/index.js +64 -0
  27. package/lib/commonjs/collapsible/index.js.map +1 -0
  28. package/lib/commonjs/collapsible/interface.js +39 -0
  29. package/lib/commonjs/collapsible/interface.js.map +1 -0
  30. package/lib/commonjs/collapsible/style.js +161 -0
  31. package/lib/commonjs/collapsible/style.js.map +1 -0
  32. package/lib/commonjs/datePicker/index.js +915 -0
  33. package/lib/commonjs/datePicker/index.js.map +1 -0
  34. package/lib/commonjs/dialog/dialogue.js +205 -0
  35. package/lib/commonjs/dialog/dialogue.js.map +1 -0
  36. package/lib/commonjs/dialog/index.js +393 -0
  37. package/lib/commonjs/dialog/index.js.map +1 -0
  38. package/lib/commonjs/dialog/useDialogue.js +117 -0
  39. package/lib/commonjs/dialog/useDialogue.js.map +1 -0
  40. package/lib/commonjs/divider/index.js +37 -0
  41. package/lib/commonjs/divider/index.js.map +1 -0
  42. package/lib/commonjs/drawer/Drawer.js +587 -0
  43. package/lib/commonjs/drawer/Drawer.js.map +1 -0
  44. package/lib/commonjs/drawer/index.js +51 -0
  45. package/lib/commonjs/drawer/index.js.map +1 -0
  46. package/lib/commonjs/drawer/interface.js +84 -0
  47. package/lib/commonjs/drawer/interface.js.map +1 -0
  48. package/lib/commonjs/dropdown/index.js +859 -0
  49. package/lib/commonjs/dropdown/index.js.map +1 -0
  50. package/lib/commonjs/emptyState/index.js +231 -0
  51. package/lib/commonjs/emptyState/index.js.map +1 -0
  52. package/lib/commonjs/form/index.js +2 -0
  53. package/lib/commonjs/form/index.js.map +1 -0
  54. package/lib/commonjs/header/index.js +147 -0
  55. package/lib/commonjs/header/index.js.map +1 -0
  56. package/lib/commonjs/header/statusBar/index.js +20 -0
  57. package/lib/commonjs/header/statusBar/index.js.map +1 -0
  58. package/lib/commonjs/icons/backArrow.js +42 -0
  59. package/lib/commonjs/icons/backArrow.js.map +1 -0
  60. package/lib/commonjs/icons/bellFill.js +43 -0
  61. package/lib/commonjs/icons/bellFill.js.map +1 -0
  62. package/lib/commonjs/icons/bellOutline.js +43 -0
  63. package/lib/commonjs/icons/bellOutline.js.map +1 -0
  64. package/lib/commonjs/icons/checkmark.js +36 -0
  65. package/lib/commonjs/icons/checkmark.js.map +1 -0
  66. package/lib/commonjs/icons/delete.js +53 -0
  67. package/lib/commonjs/icons/delete.js.map +1 -0
  68. package/lib/commonjs/icons/downChevron.js +36 -0
  69. package/lib/commonjs/icons/downChevron.js.map +1 -0
  70. package/lib/commonjs/icons/error.js +41 -0
  71. package/lib/commonjs/icons/error.js.map +1 -0
  72. package/lib/commonjs/icons/forwardArrow.js +42 -0
  73. package/lib/commonjs/icons/forwardArrow.js.map +1 -0
  74. package/lib/commonjs/icons/index.js +111 -0
  75. package/lib/commonjs/icons/index.js.map +1 -0
  76. package/lib/commonjs/icons/info.js +48 -0
  77. package/lib/commonjs/icons/info.js.map +1 -0
  78. package/lib/commonjs/icons/leftChevron.js +36 -0
  79. package/lib/commonjs/icons/leftChevron.js.map +1 -0
  80. package/lib/commonjs/icons/rightChevron.js +36 -0
  81. package/lib/commonjs/icons/rightChevron.js.map +1 -0
  82. package/lib/commonjs/icons/save.js +50 -0
  83. package/lib/commonjs/icons/save.js.map +1 -0
  84. package/lib/commonjs/icons/success.js +43 -0
  85. package/lib/commonjs/icons/success.js.map +1 -0
  86. package/lib/commonjs/icons/upChevron.js +36 -0
  87. package/lib/commonjs/icons/upChevron.js.map +1 -0
  88. package/lib/commonjs/icons/warning.js +48 -0
  89. package/lib/commonjs/icons/warning.js.map +1 -0
  90. package/lib/commonjs/image/index.js +47 -0
  91. package/lib/commonjs/image/index.js.map +1 -0
  92. package/lib/commonjs/index.js +519 -138
  93. package/lib/commonjs/index.js.map +1 -1
  94. package/lib/commonjs/input/index.js +488 -0
  95. package/lib/commonjs/input/index.js.map +1 -0
  96. package/lib/commonjs/loading/circular.js +116 -0
  97. package/lib/commonjs/loading/circular.js.map +1 -0
  98. package/lib/commonjs/loading/index.js +34 -0
  99. package/lib/commonjs/loading/index.js.map +1 -0
  100. package/lib/commonjs/loading/loader.js +247 -0
  101. package/lib/commonjs/loading/loader.js.map +1 -0
  102. package/lib/commonjs/loading/spinner.js +90 -0
  103. package/lib/commonjs/loading/spinner.js.map +1 -0
  104. package/lib/commonjs/loading/useLoader.js +62 -0
  105. package/lib/commonjs/loading/useLoader.js.map +1 -0
  106. package/lib/commonjs/loading/useLoaderBinding.js +34 -0
  107. package/lib/commonjs/loading/useLoaderBinding.js.map +1 -0
  108. package/lib/commonjs/notification/index.js +269 -0
  109. package/lib/commonjs/notification/index.js.map +1 -0
  110. package/lib/commonjs/notification/useNotification.js +62 -0
  111. package/lib/commonjs/notification/useNotification.js.map +1 -0
  112. package/lib/commonjs/page/index.js +22 -0
  113. package/lib/commonjs/page/index.js.map +1 -0
  114. package/lib/commonjs/popup/Popup.js +332 -0
  115. package/lib/commonjs/popup/Popup.js.map +1 -0
  116. package/lib/commonjs/popup/helpers.js +168 -0
  117. package/lib/commonjs/popup/helpers.js.map +1 -0
  118. package/lib/commonjs/popup/index.js +51 -0
  119. package/lib/commonjs/popup/index.js.map +1 -0
  120. package/lib/commonjs/popup/interface.js +45 -0
  121. package/lib/commonjs/popup/interface.js.map +1 -0
  122. package/lib/commonjs/portal/GlobalPortalProvider.js +68 -0
  123. package/lib/commonjs/portal/GlobalPortalProvider.js.map +1 -0
  124. package/lib/commonjs/portal/PortalContext.js +38 -0
  125. package/lib/commonjs/portal/PortalContext.js.map +1 -0
  126. package/lib/commonjs/portal/PortalInstance.js +123 -0
  127. package/lib/commonjs/portal/PortalInstance.js.map +1 -0
  128. package/lib/commonjs/portal/PortalManager.js +87 -0
  129. package/lib/commonjs/portal/PortalManager.js.map +1 -0
  130. package/lib/commonjs/portal/PortalRenderer.js +57 -0
  131. package/lib/commonjs/portal/PortalRenderer.js.map +1 -0
  132. package/lib/commonjs/portal/index.js +54 -0
  133. package/lib/commonjs/portal/index.js.map +1 -0
  134. package/lib/commonjs/portal/portal.test.js +154 -0
  135. package/lib/commonjs/portal/portal.test.js.map +1 -0
  136. package/lib/commonjs/portal/types.js +6 -0
  137. package/lib/commonjs/portal/types.js.map +1 -0
  138. package/lib/commonjs/pressable/index.js +18 -0
  139. package/lib/commonjs/pressable/index.js.map +1 -0
  140. package/lib/commonjs/progressBar/index.js +512 -0
  141. package/lib/commonjs/progressBar/index.js.map +1 -0
  142. package/lib/commonjs/radio/index.js +368 -0
  143. package/lib/commonjs/radio/index.js.map +1 -0
  144. package/lib/commonjs/safeAreaProvider/index.js +12 -0
  145. package/lib/commonjs/safeAreaProvider/index.js.map +1 -0
  146. package/lib/commonjs/safeAreaView/index.js +14 -0
  147. package/lib/commonjs/safeAreaView/index.js.map +1 -0
  148. package/lib/commonjs/scrollView/index.js +14 -0
  149. package/lib/commonjs/scrollView/index.js.map +1 -0
  150. package/lib/commonjs/searchBar/index.js +356 -0
  151. package/lib/commonjs/searchBar/index.js.map +1 -0
  152. package/lib/commonjs/seperator/index.js +44 -0
  153. package/lib/commonjs/seperator/index.js.map +1 -0
  154. package/lib/commonjs/services/index.js +185 -0
  155. package/lib/commonjs/services/index.js.map +1 -0
  156. package/lib/commonjs/shape/index.js +36 -0
  157. package/lib/commonjs/shape/index.js.map +1 -0
  158. package/lib/commonjs/skeleton/index.js +430 -0
  159. package/lib/commonjs/skeleton/index.js.map +1 -0
  160. package/lib/commonjs/slider/index.js +499 -0
  161. package/lib/commonjs/slider/index.js.map +1 -0
  162. package/lib/commonjs/spacer/index.js +14 -0
  163. package/lib/commonjs/spacer/index.js.map +1 -0
  164. package/lib/commonjs/stack/index.js +52 -0
  165. package/lib/commonjs/stack/index.js.map +1 -0
  166. package/lib/commonjs/switch/Switch.js +289 -0
  167. package/lib/commonjs/switch/Switch.js.map +1 -0
  168. package/lib/commonjs/switch/_index.js +118 -0
  169. package/lib/commonjs/switch/_index.js.map +1 -0
  170. package/lib/commonjs/switch/index.js +26 -0
  171. package/lib/commonjs/switch/index.js.map +1 -0
  172. package/lib/commonjs/switch/interface.js +47 -0
  173. package/lib/commonjs/switch/interface.js.map +1 -0
  174. package/lib/commonjs/tabBar/TabBar.js +409 -0
  175. package/lib/commonjs/tabBar/TabBar.js.map +1 -0
  176. package/lib/commonjs/tabBar/TabBarUsage.js +441 -0
  177. package/lib/commonjs/tabBar/TabBarUsage.js.map +1 -0
  178. package/lib/commonjs/tabBar/index.js +26 -0
  179. package/lib/commonjs/tabBar/index.js.map +1 -0
  180. package/lib/commonjs/tabBar/interface.js +43 -0
  181. package/lib/commonjs/tabBar/interface.js.map +1 -0
  182. package/lib/commonjs/text/index.js +65 -0
  183. package/lib/commonjs/text/index.js.map +1 -0
  184. package/lib/commonjs/timeline/index.js +264 -0
  185. package/lib/commonjs/timeline/index.js.map +1 -0
  186. package/lib/commonjs/toast/index.js +203 -0
  187. package/lib/commonjs/toast/index.js.map +1 -0
  188. package/lib/commonjs/toast/useToast.js +82 -0
  189. package/lib/commonjs/toast/useToast.js.map +1 -0
  190. package/lib/commonjs/utiles/createIcon.js +49 -0
  191. package/lib/commonjs/utiles/createIcon.js.map +1 -0
  192. package/lib/commonjs/utiles/fontStyles.js +36 -0
  193. package/lib/commonjs/utiles/fontStyles.js.map +1 -0
  194. package/lib/commonjs/utiles/position.js +98 -0
  195. package/lib/commonjs/utiles/position.js.map +1 -0
  196. package/lib/commonjs/utiles/statusBar.js +425 -0
  197. package/lib/commonjs/utiles/statusBar.js.map +1 -0
  198. package/lib/commonjs/utiles/styled.js +42 -0
  199. package/lib/commonjs/utiles/styled.js.map +1 -0
  200. package/lib/commonjs/utiles/styles.js +30 -0
  201. package/lib/commonjs/utiles/styles.js.map +1 -0
  202. package/lib/commonjs/utiles/theme.js +680 -0
  203. package/lib/commonjs/utiles/theme.js.map +1 -0
  204. package/lib/commonjs/utiles/validators.js +31 -0
  205. package/lib/commonjs/utiles/validators.js.map +1 -0
  206. package/lib/commonjs/utiles/viewStyleProps.js +6 -0
  207. package/lib/commonjs/utiles/viewStyleProps.js.map +1 -0
  208. package/lib/commonjs/utiles/viewStyleVariants.js +546 -0
  209. package/lib/commonjs/utiles/viewStyleVariants.js.map +1 -0
  210. package/lib/module/actionSheet/actionSheet.js +368 -0
  211. package/lib/module/actionSheet/actionSheet.js.map +1 -0
  212. package/lib/module/actionSheet/index.js +5 -0
  213. package/lib/module/actionSheet/index.js.map +1 -0
  214. package/lib/module/actionSheet/useActionSheet.js +85 -0
  215. package/lib/module/actionSheet/useActionSheet.js.map +1 -0
  216. package/lib/module/badge/index.js +147 -0
  217. package/lib/module/badge/index.js.map +1 -0
  218. package/lib/module/barChart/index.js +292 -0
  219. package/lib/module/barChart/index.js.map +1 -0
  220. package/lib/module/button/index.js +224 -0
  221. package/lib/module/button/index.js.map +1 -0
  222. package/lib/module/card/index.js +198 -0
  223. package/lib/module/card/index.js.map +1 -0
  224. package/lib/module/checkBox/index.js +86 -0
  225. package/lib/module/checkBox/index.js.map +1 -0
  226. package/lib/module/chips/index.js +232 -0
  227. package/lib/module/chips/index.js.map +1 -0
  228. package/lib/module/circularProgress/index.js +259 -0
  229. package/lib/module/circularProgress/index.js.map +1 -0
  230. package/lib/module/collapsible/Collapse.js +288 -0
  231. package/lib/module/collapsible/Collapse.js.map +1 -0
  232. package/lib/module/collapsible/CollapseGroup.js +133 -0
  233. package/lib/module/collapsible/CollapseGroup.js.map +1 -0
  234. package/lib/module/collapsible/index.js +40 -0
  235. package/lib/module/collapsible/index.js.map +1 -0
  236. package/lib/module/collapsible/interface.js +35 -0
  237. package/lib/module/collapsible/interface.js.map +1 -0
  238. package/lib/module/collapsible/style.js +153 -0
  239. package/lib/module/collapsible/style.js.map +1 -0
  240. package/lib/module/datePicker/index.js +908 -0
  241. package/lib/module/datePicker/index.js.map +1 -0
  242. package/lib/module/dialog/dialogue.js +189 -0
  243. package/lib/module/dialog/dialogue.js.map +1 -0
  244. package/lib/module/dialog/index.js +386 -0
  245. package/lib/module/dialog/index.js.map +1 -0
  246. package/lib/module/dialog/useDialogue.js +113 -0
  247. package/lib/module/dialog/useDialogue.js.map +1 -0
  248. package/lib/module/divider/index.js +34 -0
  249. package/lib/module/divider/index.js.map +1 -0
  250. package/lib/module/drawer/Drawer.js +583 -0
  251. package/lib/module/drawer/Drawer.js.map +1 -0
  252. package/lib/module/drawer/index.js +60 -0
  253. package/lib/module/drawer/index.js.map +1 -0
  254. package/lib/module/drawer/interface.js +80 -0
  255. package/lib/module/drawer/interface.js.map +1 -0
  256. package/lib/module/dropdown/index.js +854 -0
  257. package/lib/module/dropdown/index.js.map +1 -0
  258. package/lib/module/emptyState/index.js +225 -0
  259. package/lib/module/emptyState/index.js.map +1 -0
  260. package/lib/module/form/index.js +2 -0
  261. package/lib/module/form/index.js.map +1 -0
  262. package/lib/module/header/index.js +143 -0
  263. package/lib/module/header/index.js.map +1 -0
  264. package/lib/module/header/statusBar/index.js +15 -0
  265. package/lib/module/header/statusBar/index.js.map +1 -0
  266. package/lib/module/icons/backArrow.js +37 -0
  267. package/lib/module/icons/backArrow.js.map +1 -0
  268. package/lib/module/icons/bellFill.js +38 -0
  269. package/lib/module/icons/bellFill.js.map +1 -0
  270. package/lib/module/icons/bellOutline.js +38 -0
  271. package/lib/module/icons/bellOutline.js.map +1 -0
  272. package/lib/module/icons/checkmark.js +31 -0
  273. package/lib/module/icons/checkmark.js.map +1 -0
  274. package/lib/module/icons/delete.js +48 -0
  275. package/lib/module/icons/delete.js.map +1 -0
  276. package/lib/module/icons/downChevron.js +31 -0
  277. package/lib/module/icons/downChevron.js.map +1 -0
  278. package/lib/module/icons/error.js +36 -0
  279. package/lib/module/icons/error.js.map +1 -0
  280. package/lib/module/icons/forwardArrow.js +37 -0
  281. package/lib/module/icons/forwardArrow.js.map +1 -0
  282. package/lib/module/icons/index.js +18 -0
  283. package/lib/module/icons/index.js.map +1 -0
  284. package/lib/module/icons/info.js +43 -0
  285. package/lib/module/icons/info.js.map +1 -0
  286. package/lib/module/icons/leftChevron.js +31 -0
  287. package/lib/module/icons/leftChevron.js.map +1 -0
  288. package/lib/module/icons/rightChevron.js +31 -0
  289. package/lib/module/icons/rightChevron.js.map +1 -0
  290. package/lib/module/icons/save.js +45 -0
  291. package/lib/module/icons/save.js.map +1 -0
  292. package/lib/module/icons/success.js +38 -0
  293. package/lib/module/icons/success.js.map +1 -0
  294. package/lib/module/icons/upChevron.js +31 -0
  295. package/lib/module/icons/upChevron.js.map +1 -0
  296. package/lib/module/icons/warning.js +43 -0
  297. package/lib/module/icons/warning.js.map +1 -0
  298. package/lib/module/image/index.js +43 -0
  299. package/lib/module/image/index.js.map +1 -0
  300. package/lib/module/index.js +56 -24
  301. package/lib/module/index.js.map +1 -1
  302. package/lib/module/input/index.js +483 -0
  303. package/lib/module/input/index.js.map +1 -0
  304. package/lib/module/loading/circular.js +111 -0
  305. package/lib/module/loading/circular.js.map +1 -0
  306. package/lib/module/loading/index.js +7 -0
  307. package/lib/module/loading/index.js.map +1 -0
  308. package/lib/module/loading/loader.js +242 -0
  309. package/lib/module/loading/loader.js.map +1 -0
  310. package/lib/module/loading/spinner.js +85 -0
  311. package/lib/module/loading/spinner.js.map +1 -0
  312. package/lib/module/loading/useLoader.js +58 -0
  313. package/lib/module/loading/useLoader.js.map +1 -0
  314. package/lib/module/loading/useLoaderBinding.js +28 -0
  315. package/lib/module/loading/useLoaderBinding.js.map +1 -0
  316. package/lib/module/notification/index.js +253 -0
  317. package/lib/module/notification/index.js.map +1 -0
  318. package/lib/module/notification/useNotification.js +58 -0
  319. package/lib/module/notification/useNotification.js.map +1 -0
  320. package/lib/module/page/index.js +17 -0
  321. package/lib/module/page/index.js.map +1 -0
  322. package/lib/module/popup/Popup.js +328 -0
  323. package/lib/module/popup/Popup.js.map +1 -0
  324. package/lib/module/popup/helpers.js +159 -0
  325. package/lib/module/popup/helpers.js.map +1 -0
  326. package/lib/module/popup/index.js +45 -0
  327. package/lib/module/popup/index.js.map +1 -0
  328. package/lib/module/popup/interface.js +41 -0
  329. package/lib/module/popup/interface.js.map +1 -0
  330. package/lib/module/portal/GlobalPortalProvider.js +62 -0
  331. package/lib/module/portal/GlobalPortalProvider.js.map +1 -0
  332. package/lib/module/portal/PortalContext.js +33 -0
  333. package/lib/module/portal/PortalContext.js.map +1 -0
  334. package/lib/module/portal/PortalInstance.js +118 -0
  335. package/lib/module/portal/PortalInstance.js.map +1 -0
  336. package/lib/module/portal/PortalManager.js +81 -0
  337. package/lib/module/portal/PortalManager.js.map +1 -0
  338. package/lib/module/portal/PortalRenderer.js +51 -0
  339. package/lib/module/portal/PortalRenderer.js.map +1 -0
  340. package/lib/module/portal/index.js +56 -0
  341. package/lib/module/portal/index.js.map +1 -0
  342. package/lib/module/portal/portal.test.js +155 -0
  343. package/lib/module/portal/portal.test.js.map +1 -0
  344. package/lib/module/portal/types.js +4 -0
  345. package/lib/module/portal/types.js.map +1 -0
  346. package/lib/module/pressable/index.js +15 -0
  347. package/lib/module/pressable/index.js.map +1 -0
  348. package/lib/module/progressBar/index.js +506 -0
  349. package/lib/module/progressBar/index.js.map +1 -0
  350. package/lib/module/radio/index.js +361 -0
  351. package/lib/module/radio/index.js.map +1 -0
  352. package/lib/module/safeAreaProvider/index.js +9 -0
  353. package/lib/module/safeAreaProvider/index.js.map +1 -0
  354. package/lib/module/safeAreaView/index.js +11 -0
  355. package/lib/module/safeAreaView/index.js.map +1 -0
  356. package/lib/module/scrollView/index.js +11 -0
  357. package/lib/module/scrollView/index.js.map +1 -0
  358. package/lib/module/searchBar/index.js +350 -0
  359. package/lib/module/searchBar/index.js.map +1 -0
  360. package/lib/module/seperator/index.js +40 -0
  361. package/lib/module/seperator/index.js.map +1 -0
  362. package/lib/module/services/index.js +181 -0
  363. package/lib/module/services/index.js.map +1 -0
  364. package/lib/module/shape/index.js +33 -0
  365. package/lib/module/shape/index.js.map +1 -0
  366. package/lib/module/skeleton/index.js +425 -0
  367. package/lib/module/skeleton/index.js.map +1 -0
  368. package/lib/module/slider/index.js +493 -0
  369. package/lib/module/slider/index.js.map +1 -0
  370. package/lib/module/spacer/index.js +11 -0
  371. package/lib/module/spacer/index.js.map +1 -0
  372. package/lib/module/stack/index.js +50 -0
  373. package/lib/module/stack/index.js.map +1 -0
  374. package/lib/module/switch/Switch.js +285 -0
  375. package/lib/module/switch/Switch.js.map +1 -0
  376. package/lib/module/switch/_index.js +114 -0
  377. package/lib/module/switch/_index.js.map +1 -0
  378. package/lib/module/switch/index.js +64 -0
  379. package/lib/module/switch/index.js.map +1 -0
  380. package/lib/module/switch/interface.js +43 -0
  381. package/lib/module/switch/interface.js.map +1 -0
  382. package/lib/module/tabBar/TabBar.js +405 -0
  383. package/lib/module/tabBar/TabBar.js.map +1 -0
  384. package/lib/module/tabBar/TabBarUsage.js +437 -0
  385. package/lib/module/tabBar/TabBarUsage.js.map +1 -0
  386. package/lib/module/tabBar/index.js +54 -0
  387. package/lib/module/tabBar/index.js.map +1 -0
  388. package/lib/module/tabBar/interface.js +39 -0
  389. package/lib/module/tabBar/interface.js.map +1 -0
  390. package/lib/module/text/index.js +62 -0
  391. package/lib/module/text/index.js.map +1 -0
  392. package/lib/module/timeline/index.js +258 -0
  393. package/lib/module/timeline/index.js.map +1 -0
  394. package/lib/module/toast/index.js +187 -0
  395. package/lib/module/toast/index.js.map +1 -0
  396. package/lib/module/toast/useToast.js +78 -0
  397. package/lib/module/toast/useToast.js.map +1 -0
  398. package/lib/module/utiles/createIcon.js +45 -0
  399. package/lib/module/utiles/createIcon.js.map +1 -0
  400. package/lib/module/utiles/fontStyles.js +31 -0
  401. package/lib/module/utiles/fontStyles.js.map +1 -0
  402. package/lib/module/utiles/position.js +94 -0
  403. package/lib/module/utiles/position.js.map +1 -0
  404. package/lib/module/utiles/statusBar.js +401 -0
  405. package/lib/module/utiles/statusBar.js.map +1 -0
  406. package/lib/module/utiles/styled.js +37 -0
  407. package/lib/module/utiles/styled.js.map +1 -0
  408. package/lib/module/utiles/styles.js +27 -0
  409. package/lib/module/utiles/styles.js.map +1 -0
  410. package/lib/module/{package → utiles}/theme.js +305 -17
  411. package/lib/module/utiles/theme.js.map +1 -0
  412. package/lib/module/utiles/validators.js +24 -0
  413. package/lib/module/utiles/validators.js.map +1 -0
  414. package/lib/module/utiles/viewStyleProps.js +4 -0
  415. package/lib/module/utiles/viewStyleProps.js.map +1 -0
  416. package/lib/module/utiles/viewStyleVariants.js +542 -0
  417. package/lib/module/utiles/viewStyleVariants.js.map +1 -0
  418. package/lib/typescript/actionSheet/actionSheet.d.ts +82 -0
  419. package/lib/typescript/actionSheet/actionSheet.d.ts.map +1 -0
  420. package/lib/typescript/actionSheet/index.d.ts +4 -0
  421. package/lib/typescript/actionSheet/index.d.ts.map +1 -0
  422. package/lib/typescript/actionSheet/useActionSheet.d.ts +82 -0
  423. package/lib/typescript/actionSheet/useActionSheet.d.ts.map +1 -0
  424. package/lib/typescript/badge/index.d.ts +37 -0
  425. package/lib/typescript/badge/index.d.ts.map +1 -0
  426. package/lib/typescript/barChart/index.d.ts +125 -0
  427. package/lib/typescript/barChart/index.d.ts.map +1 -0
  428. package/lib/typescript/button/index.d.ts +86 -0
  429. package/lib/typescript/button/index.d.ts.map +1 -0
  430. package/lib/typescript/card/index.d.ts +53 -0
  431. package/lib/typescript/card/index.d.ts.map +1 -0
  432. package/lib/typescript/checkBox/index.d.ts +28 -0
  433. package/lib/typescript/checkBox/index.d.ts.map +1 -0
  434. package/lib/typescript/chips/index.d.ts +104 -0
  435. package/lib/typescript/chips/index.d.ts.map +1 -0
  436. package/lib/typescript/circularProgress/index.d.ts +36 -0
  437. package/lib/typescript/circularProgress/index.d.ts.map +1 -0
  438. package/lib/typescript/collapsible/Collapse.d.ts +5 -0
  439. package/lib/typescript/collapsible/Collapse.d.ts.map +1 -0
  440. package/lib/typescript/collapsible/CollapseGroup.d.ts +49 -0
  441. package/lib/typescript/collapsible/CollapseGroup.d.ts.map +1 -0
  442. package/lib/typescript/collapsible/index.d.ts +38 -0
  443. package/lib/typescript/collapsible/index.d.ts.map +1 -0
  444. package/lib/typescript/collapsible/interface.d.ts +116 -0
  445. package/lib/typescript/collapsible/interface.d.ts.map +1 -0
  446. package/lib/typescript/collapsible/style.d.ts +92 -0
  447. package/lib/typescript/collapsible/style.d.ts.map +1 -0
  448. package/lib/typescript/datePicker/index.d.ts +91 -0
  449. package/lib/typescript/datePicker/index.d.ts.map +1 -0
  450. package/lib/typescript/dialog/dialogue.d.ts +20 -0
  451. package/lib/typescript/dialog/dialogue.d.ts.map +1 -0
  452. package/lib/typescript/dialog/index.d.ts +175 -0
  453. package/lib/typescript/dialog/index.d.ts.map +1 -0
  454. package/lib/typescript/dialog/useDialogue.d.ts +21 -0
  455. package/lib/typescript/dialog/useDialogue.d.ts.map +1 -0
  456. package/lib/typescript/divider/index.d.ts +12 -0
  457. package/lib/typescript/divider/index.d.ts.map +1 -0
  458. package/lib/typescript/drawer/Drawer.d.ts +4 -0
  459. package/lib/typescript/drawer/Drawer.d.ts.map +1 -0
  460. package/lib/typescript/drawer/index.d.ts +48 -0
  461. package/lib/typescript/drawer/index.d.ts.map +1 -0
  462. package/lib/typescript/drawer/interface.d.ts +232 -0
  463. package/lib/typescript/drawer/interface.d.ts.map +1 -0
  464. package/lib/typescript/dropdown/index.d.ts +54 -0
  465. package/lib/typescript/dropdown/index.d.ts.map +1 -0
  466. package/lib/typescript/emptyState/index.d.ts +57 -0
  467. package/lib/typescript/emptyState/index.d.ts.map +1 -0
  468. package/lib/typescript/form/index.d.ts +1 -0
  469. package/lib/typescript/form/index.d.ts.map +1 -0
  470. package/lib/typescript/header/index.d.ts +44 -0
  471. package/lib/typescript/header/index.d.ts.map +1 -0
  472. package/lib/typescript/header/statusBar/index.d.ts +6 -0
  473. package/lib/typescript/header/statusBar/index.d.ts.map +1 -0
  474. package/lib/typescript/icons/backArrow.d.ts +12 -0
  475. package/lib/typescript/icons/backArrow.d.ts.map +1 -0
  476. package/lib/typescript/icons/bellFill.d.ts +12 -0
  477. package/lib/typescript/icons/bellFill.d.ts.map +1 -0
  478. package/lib/typescript/icons/bellOutline.d.ts +12 -0
  479. package/lib/typescript/icons/bellOutline.d.ts.map +1 -0
  480. package/lib/typescript/icons/checkmark.d.ts +12 -0
  481. package/lib/typescript/icons/checkmark.d.ts.map +1 -0
  482. package/lib/typescript/icons/delete.d.ts +12 -0
  483. package/lib/typescript/icons/delete.d.ts.map +1 -0
  484. package/lib/typescript/icons/downChevron.d.ts +12 -0
  485. package/lib/typescript/icons/downChevron.d.ts.map +1 -0
  486. package/lib/typescript/icons/error.d.ts +12 -0
  487. package/lib/typescript/icons/error.d.ts.map +1 -0
  488. package/lib/typescript/icons/forwardArrow.d.ts +12 -0
  489. package/lib/typescript/icons/forwardArrow.d.ts.map +1 -0
  490. package/lib/typescript/icons/index.d.ts +16 -0
  491. package/lib/typescript/icons/index.d.ts.map +1 -0
  492. package/lib/typescript/icons/info.d.ts +12 -0
  493. package/lib/typescript/icons/info.d.ts.map +1 -0
  494. package/lib/typescript/icons/leftChevron.d.ts +12 -0
  495. package/lib/typescript/icons/leftChevron.d.ts.map +1 -0
  496. package/lib/typescript/icons/rightChevron.d.ts +12 -0
  497. package/lib/typescript/icons/rightChevron.d.ts.map +1 -0
  498. package/lib/typescript/icons/save.d.ts +12 -0
  499. package/lib/typescript/icons/save.d.ts.map +1 -0
  500. package/lib/typescript/icons/success.d.ts +12 -0
  501. package/lib/typescript/icons/success.d.ts.map +1 -0
  502. package/lib/typescript/icons/upChevron.d.ts +12 -0
  503. package/lib/typescript/icons/upChevron.d.ts.map +1 -0
  504. package/lib/typescript/icons/warning.d.ts +12 -0
  505. package/lib/typescript/icons/warning.d.ts.map +1 -0
  506. package/lib/typescript/image/index.d.ts +15 -0
  507. package/lib/typescript/image/index.d.ts.map +1 -0
  508. package/lib/typescript/index.d.ts +56 -0
  509. package/lib/typescript/index.d.ts.map +1 -0
  510. package/lib/typescript/input/index.d.ts +89 -0
  511. package/lib/typescript/input/index.d.ts.map +1 -0
  512. package/lib/typescript/loading/circular.d.ts +10 -0
  513. package/lib/typescript/loading/circular.d.ts.map +1 -0
  514. package/lib/typescript/loading/index.d.ts +7 -0
  515. package/lib/typescript/loading/index.d.ts.map +1 -0
  516. package/lib/typescript/loading/loader.d.ts +23 -0
  517. package/lib/typescript/loading/loader.d.ts.map +1 -0
  518. package/lib/typescript/loading/spinner.d.ts +22 -0
  519. package/lib/typescript/loading/spinner.d.ts.map +1 -0
  520. package/lib/typescript/loading/useLoader.d.ts +27 -0
  521. package/lib/typescript/loading/useLoader.d.ts.map +1 -0
  522. package/lib/typescript/loading/useLoaderBinding.d.ts +9 -0
  523. package/lib/typescript/loading/useLoaderBinding.d.ts.map +1 -0
  524. package/lib/typescript/notification/index.d.ts +21 -0
  525. package/lib/typescript/notification/index.d.ts.map +1 -0
  526. package/lib/typescript/notification/useNotification.d.ts +30 -0
  527. package/lib/typescript/notification/useNotification.d.ts.map +1 -0
  528. package/lib/typescript/page/index.d.ts +12 -0
  529. package/lib/typescript/page/index.d.ts.map +1 -0
  530. package/lib/typescript/popup/Popup.d.ts +4 -0
  531. package/lib/typescript/popup/Popup.d.ts.map +1 -0
  532. package/lib/typescript/popup/helpers.d.ts +9 -0
  533. package/lib/typescript/popup/helpers.d.ts.map +1 -0
  534. package/lib/typescript/popup/index.d.ts +43 -0
  535. package/lib/typescript/popup/index.d.ts.map +1 -0
  536. package/lib/typescript/popup/interface.d.ts +103 -0
  537. package/lib/typescript/popup/interface.d.ts.map +1 -0
  538. package/lib/typescript/portal/GlobalPortalProvider.d.ts +44 -0
  539. package/lib/typescript/portal/GlobalPortalProvider.d.ts.map +1 -0
  540. package/lib/typescript/portal/PortalContext.d.ts +25 -0
  541. package/lib/typescript/portal/PortalContext.d.ts.map +1 -0
  542. package/lib/typescript/portal/PortalInstance.d.ts +72 -0
  543. package/lib/typescript/portal/PortalInstance.d.ts.map +1 -0
  544. package/lib/typescript/portal/PortalManager.d.ts +7 -0
  545. package/lib/typescript/portal/PortalManager.d.ts.map +1 -0
  546. package/lib/typescript/portal/PortalRenderer.d.ts +27 -0
  547. package/lib/typescript/portal/PortalRenderer.d.ts.map +1 -0
  548. package/lib/typescript/portal/index.d.ts +43 -0
  549. package/lib/typescript/portal/index.d.ts.map +1 -0
  550. package/lib/typescript/portal/portal.test.d.ts +11 -0
  551. package/lib/typescript/portal/portal.test.d.ts.map +1 -0
  552. package/lib/typescript/portal/types.d.ts +30 -0
  553. package/lib/typescript/portal/types.d.ts.map +1 -0
  554. package/lib/typescript/pressable/index.d.ts +8 -0
  555. package/lib/typescript/pressable/index.d.ts.map +1 -0
  556. package/lib/typescript/progressBar/index.d.ts +155 -0
  557. package/lib/typescript/progressBar/index.d.ts.map +1 -0
  558. package/lib/typescript/radio/index.d.ts +144 -0
  559. package/lib/typescript/radio/index.d.ts.map +1 -0
  560. package/lib/typescript/safeAreaProvider/index.d.ts +7 -0
  561. package/lib/typescript/safeAreaProvider/index.d.ts.map +1 -0
  562. package/lib/typescript/safeAreaView/index.d.ts +8 -0
  563. package/lib/typescript/safeAreaView/index.d.ts.map +1 -0
  564. package/lib/typescript/scrollView/index.d.ts +8 -0
  565. package/lib/typescript/scrollView/index.d.ts.map +1 -0
  566. package/lib/typescript/searchBar/index.d.ts +70 -0
  567. package/lib/typescript/searchBar/index.d.ts.map +1 -0
  568. package/lib/typescript/seperator/index.d.ts +13 -0
  569. package/lib/typescript/seperator/index.d.ts.map +1 -0
  570. package/lib/typescript/services/index.d.ts +56 -0
  571. package/lib/typescript/services/index.d.ts.map +1 -0
  572. package/lib/typescript/shape/index.d.ts +11 -0
  573. package/lib/typescript/shape/index.d.ts.map +1 -0
  574. package/lib/typescript/skeleton/index.d.ts +63 -0
  575. package/lib/typescript/skeleton/index.d.ts.map +1 -0
  576. package/lib/typescript/slider/index.d.ts +60 -0
  577. package/lib/typescript/slider/index.d.ts.map +1 -0
  578. package/lib/typescript/spacer/index.d.ts +8 -0
  579. package/lib/typescript/spacer/index.d.ts.map +1 -0
  580. package/lib/typescript/stack/index.d.ts +24 -0
  581. package/lib/typescript/stack/index.d.ts.map +1 -0
  582. package/lib/typescript/switch/Switch.d.ts +6 -0
  583. package/lib/typescript/switch/Switch.d.ts.map +1 -0
  584. package/lib/typescript/switch/_index.d.ts +11 -0
  585. package/lib/typescript/switch/_index.d.ts.map +1 -0
  586. package/lib/typescript/switch/index.d.ts +62 -0
  587. package/lib/typescript/switch/index.d.ts.map +1 -0
  588. package/lib/typescript/switch/interface.d.ts +74 -0
  589. package/lib/typescript/switch/interface.d.ts.map +1 -0
  590. package/lib/typescript/tabBar/TabBar.d.ts +5 -0
  591. package/lib/typescript/tabBar/TabBar.d.ts.map +1 -0
  592. package/lib/typescript/tabBar/TabBarUsage.d.ts +6 -0
  593. package/lib/typescript/tabBar/TabBarUsage.d.ts.map +1 -0
  594. package/lib/typescript/tabBar/index.d.ts +52 -0
  595. package/lib/typescript/tabBar/index.d.ts.map +1 -0
  596. package/lib/typescript/tabBar/interface.d.ts +119 -0
  597. package/lib/typescript/tabBar/interface.d.ts.map +1 -0
  598. package/lib/typescript/text/index.d.ts +16 -0
  599. package/lib/typescript/text/index.d.ts.map +1 -0
  600. package/lib/typescript/timeline/index.d.ts +125 -0
  601. package/lib/typescript/timeline/index.d.ts.map +1 -0
  602. package/lib/typescript/toast/index.d.ts +28 -0
  603. package/lib/typescript/toast/index.d.ts.map +1 -0
  604. package/lib/typescript/toast/useToast.d.ts +31 -0
  605. package/lib/typescript/toast/useToast.d.ts.map +1 -0
  606. package/lib/typescript/utiles/createIcon.d.ts +39 -0
  607. package/lib/typescript/utiles/createIcon.d.ts.map +1 -0
  608. package/lib/typescript/utiles/fontStyles.d.ts +9 -0
  609. package/lib/typescript/utiles/fontStyles.d.ts.map +1 -0
  610. package/lib/typescript/utiles/position.d.ts +13 -0
  611. package/lib/typescript/utiles/position.d.ts.map +1 -0
  612. package/lib/typescript/utiles/statusBar.d.ts +128 -0
  613. package/lib/typescript/utiles/statusBar.d.ts.map +1 -0
  614. package/lib/typescript/utiles/styled.d.ts +14 -0
  615. package/lib/typescript/utiles/styled.d.ts.map +1 -0
  616. package/lib/typescript/utiles/styles.d.ts +31 -0
  617. package/lib/typescript/utiles/styles.d.ts.map +1 -0
  618. package/lib/typescript/utiles/theme.d.ts +918 -0
  619. package/lib/typescript/utiles/theme.d.ts.map +1 -0
  620. package/lib/typescript/utiles/validators.d.ts +14 -0
  621. package/lib/typescript/utiles/validators.d.ts.map +1 -0
  622. package/lib/typescript/utiles/viewStyleProps.d.ts +4 -0
  623. package/lib/typescript/utiles/viewStyleProps.d.ts.map +1 -0
  624. package/lib/typescript/utiles/viewStyleVariants.d.ts +121 -0
  625. package/lib/typescript/utiles/viewStyleVariants.d.ts.map +1 -0
  626. package/package.json +146 -52
  627. package/src/actionSheet/actionSheet.tsx +471 -0
  628. package/src/actionSheet/index.tsx +3 -0
  629. package/src/actionSheet/useActionSheet.tsx +140 -0
  630. package/src/badge/index.tsx +185 -0
  631. package/src/barChart/index.tsx +386 -0
  632. package/src/button/index.tsx +335 -0
  633. package/src/card/index.tsx +220 -0
  634. package/src/checkBox/index.tsx +137 -0
  635. package/src/chips/index.tsx +293 -0
  636. package/src/circularProgress/index.tsx +349 -0
  637. package/src/collapsible/Collapse.tsx +366 -0
  638. package/src/collapsible/CollapseGroup.tsx +161 -0
  639. package/src/collapsible/index.ts +48 -0
  640. package/src/collapsible/interface.ts +187 -0
  641. package/src/collapsible/style.ts +130 -0
  642. package/src/datePicker/index.tsx +961 -0
  643. package/src/dialog/dialogue.tsx +155 -0
  644. package/src/dialog/index.tsx +604 -0
  645. package/src/dialog/useDialogue.tsx +158 -0
  646. package/src/divider/index.tsx +45 -0
  647. package/src/drawer/Drawer.tsx +655 -0
  648. package/src/drawer/index.ts +60 -0
  649. package/src/drawer/interface.ts +391 -0
  650. package/src/dropdown/index.tsx +1081 -0
  651. package/src/emptyState/index.tsx +287 -0
  652. package/src/form/index.tsx +0 -0
  653. package/src/header/index.tsx +217 -0
  654. package/src/header/statusBar/index.tsx +11 -0
  655. package/src/icons/backArrow.tsx +17 -0
  656. package/src/icons/bellFill.tsx +19 -0
  657. package/src/icons/bellOutline.tsx +18 -0
  658. package/src/icons/checkmark.tsx +18 -0
  659. package/src/icons/delete.tsx +19 -0
  660. package/src/icons/downChevron.tsx +18 -0
  661. package/src/icons/error.tsx +17 -0
  662. package/src/icons/forwardArrow.tsx +19 -0
  663. package/src/icons/index.ts +15 -0
  664. package/src/icons/info.tsx +19 -0
  665. package/src/icons/leftChevron.tsx +18 -0
  666. package/src/icons/rightChevron.tsx +18 -0
  667. package/src/icons/save.tsx +18 -0
  668. package/src/icons/success.tsx +17 -0
  669. package/src/icons/upChevron.tsx +18 -0
  670. package/src/icons/warning.tsx +19 -0
  671. package/src/image/index.tsx +45 -0
  672. package/src/index.ts +72 -0
  673. package/src/input/index.tsx +668 -0
  674. package/src/loading/circular.tsx +141 -0
  675. package/src/loading/index.tsx +6 -0
  676. package/src/loading/loader.tsx +191 -0
  677. package/src/loading/spinner.tsx +126 -0
  678. package/src/loading/useLoader.tsx +78 -0
  679. package/src/loading/useLoaderBinding.tsx +38 -0
  680. package/src/notification/index.tsx +189 -0
  681. package/src/notification/useNotification.tsx +64 -0
  682. package/src/page/index.tsx +19 -0
  683. package/src/popup/Popup.tsx +398 -0
  684. package/src/popup/helpers.ts +123 -0
  685. package/src/popup/index.ts +45 -0
  686. package/src/popup/interface.ts +178 -0
  687. package/src/portal/GlobalPortalProvider.tsx +63 -0
  688. package/src/portal/PortalContext.ts +37 -0
  689. package/src/portal/PortalInstance.ts +118 -0
  690. package/src/portal/PortalManager.tsx +84 -0
  691. package/src/portal/PortalRenderer.tsx +72 -0
  692. package/src/portal/index.ts +60 -0
  693. package/src/portal/portal.test.ts +157 -0
  694. package/src/portal/types.ts +49 -0
  695. package/src/pressable/index.tsx +19 -0
  696. package/src/progressBar/index.tsx +564 -0
  697. package/src/radio/index.tsx +469 -0
  698. package/src/safeAreaProvider/index.ts +16 -0
  699. package/src/safeAreaView/index.tsx +17 -0
  700. package/src/scrollView/index.ts +15 -0
  701. package/src/searchBar/index.tsx +383 -0
  702. package/src/seperator/index.tsx +59 -0
  703. package/src/services/index.ts +149 -0
  704. package/src/shape/index.tsx +38 -0
  705. package/src/skeleton/index.tsx +350 -0
  706. package/src/slider/index.tsx +637 -0
  707. package/src/spacer/index.ts +29 -0
  708. package/src/stack/index.ts +58 -0
  709. package/src/switch/Switch.tsx +343 -0
  710. package/src/switch/_index.tsx +135 -0
  711. package/src/switch/index.ts +62 -0
  712. package/src/switch/interface.ts +120 -0
  713. package/src/tabBar/TabBar.tsx +503 -0
  714. package/src/tabBar/TabBarUsage.tsx +366 -0
  715. package/src/tabBar/index.ts +59 -0
  716. package/src/tabBar/interface.ts +196 -0
  717. package/src/text/index.ts +73 -0
  718. package/src/timeline/index.tsx +374 -0
  719. package/src/toast/index.tsx +172 -0
  720. package/src/toast/useToast.tsx +93 -0
  721. package/src/utiles/createIcon.ts +44 -0
  722. package/src/utiles/fontStyles.js +31 -0
  723. package/src/utiles/position.ts +100 -0
  724. package/src/utiles/statusBar.ts +410 -0
  725. package/src/utiles/styled.tsx +48 -0
  726. package/src/utiles/styles.ts +25 -0
  727. package/src/utiles/theme.ts +727 -0
  728. package/src/utiles/validators.ts +21 -0
  729. package/src/utiles/viewStyleProps.ts +41 -0
  730. package/src/utiles/viewStyleVariants.ts +411 -0
  731. package/lib/commonjs/assets/img/blank_1.png +0 -0
  732. package/lib/commonjs/assets/img/blank_2.png +0 -0
  733. package/lib/commonjs/assets/img/doctor.png +0 -0
  734. package/lib/commonjs/package/badge/index.js +0 -161
  735. package/lib/commonjs/package/badge/index.js.map +0 -1
  736. package/lib/commonjs/package/button/index.js +0 -99
  737. package/lib/commonjs/package/button/index.js.map +0 -1
  738. package/lib/commonjs/package/card/index.js +0 -46
  739. package/lib/commonjs/package/card/index.js.map +0 -1
  740. package/lib/commonjs/package/checkBox/index.js +0 -92
  741. package/lib/commonjs/package/checkBox/index.js.map +0 -1
  742. package/lib/commonjs/package/cycle/index.js +0 -43
  743. package/lib/commonjs/package/cycle/index.js.map +0 -1
  744. package/lib/commonjs/package/dialog/index.js +0 -216
  745. package/lib/commonjs/package/dialog/index.js.map +0 -1
  746. package/lib/commonjs/package/dropdown/index.js +0 -112
  747. package/lib/commonjs/package/dropdown/index.js.map +0 -1
  748. package/lib/commonjs/package/form/index.js +0 -190
  749. package/lib/commonjs/package/form/index.js.map +0 -1
  750. package/lib/commonjs/package/header/index.js +0 -99
  751. package/lib/commonjs/package/header/index.js.map +0 -1
  752. package/lib/commonjs/package/image/index.js +0 -326
  753. package/lib/commonjs/package/image/index.js.map +0 -1
  754. package/lib/commonjs/package/radioButton/index.js +0 -75
  755. package/lib/commonjs/package/radioButton/index.js.map +0 -1
  756. package/lib/commonjs/package/safeAreaView/index.js +0 -26
  757. package/lib/commonjs/package/safeAreaView/index.js.map +0 -1
  758. package/lib/commonjs/package/scrollView/index.js +0 -23
  759. package/lib/commonjs/package/scrollView/index.js.map +0 -1
  760. package/lib/commonjs/package/separator/index.js +0 -33
  761. package/lib/commonjs/package/separator/index.js.map +0 -1
  762. package/lib/commonjs/package/spacer/index.js +0 -18
  763. package/lib/commonjs/package/spacer/index.js.map +0 -1
  764. package/lib/commonjs/package/spinner/index.js +0 -43
  765. package/lib/commonjs/package/spinner/index.js.map +0 -1
  766. package/lib/commonjs/package/stack/__test__/index.test.js +0 -91
  767. package/lib/commonjs/package/stack/__test__/index.test.js.map +0 -1
  768. package/lib/commonjs/package/stack/index.js +0 -54
  769. package/lib/commonjs/package/stack/index.js.map +0 -1
  770. package/lib/commonjs/package/styled/__test__/index.test.js +0 -161
  771. package/lib/commonjs/package/styled/__test__/index.test.js.map +0 -1
  772. package/lib/commonjs/package/styled/index.js +0 -42
  773. package/lib/commonjs/package/styled/index.js.map +0 -1
  774. package/lib/commonjs/package/switch/index.js +0 -43
  775. package/lib/commonjs/package/switch/index.js.map +0 -1
  776. package/lib/commonjs/package/text/index.js +0 -81
  777. package/lib/commonjs/package/text/index.js.map +0 -1
  778. package/lib/commonjs/package/theme.js +0 -385
  779. package/lib/commonjs/package/theme.js.map +0 -1
  780. package/lib/commonjs/package/utils/index.js +0 -130
  781. package/lib/commonjs/package/utils/index.js.map +0 -1
  782. package/lib/commonjs/package/utils/statusBar.js +0 -70
  783. package/lib/commonjs/package/utils/statusBar.js.map +0 -1
  784. package/lib/commonjs/package/utils/validator.js +0 -40
  785. package/lib/commonjs/package/utils/validator.js.map +0 -1
  786. package/lib/commonjs/styled.code-workspace +0 -9
  787. package/lib/module/assets/img/blank_1.png +0 -0
  788. package/lib/module/assets/img/blank_2.png +0 -0
  789. package/lib/module/assets/img/doctor.png +0 -0
  790. package/lib/module/package/badge/index.js +0 -153
  791. package/lib/module/package/badge/index.js.map +0 -1
  792. package/lib/module/package/button/index.js +0 -92
  793. package/lib/module/package/button/index.js.map +0 -1
  794. package/lib/module/package/card/index.js +0 -39
  795. package/lib/module/package/card/index.js.map +0 -1
  796. package/lib/module/package/checkBox/index.js +0 -83
  797. package/lib/module/package/checkBox/index.js.map +0 -1
  798. package/lib/module/package/cycle/index.js +0 -36
  799. package/lib/module/package/cycle/index.js.map +0 -1
  800. package/lib/module/package/dialog/index.js +0 -206
  801. package/lib/module/package/dialog/index.js.map +0 -1
  802. package/lib/module/package/dropdown/index.js +0 -102
  803. package/lib/module/package/dropdown/index.js.map +0 -1
  804. package/lib/module/package/form/index.js +0 -182
  805. package/lib/module/package/form/index.js.map +0 -1
  806. package/lib/module/package/header/index.js +0 -93
  807. package/lib/module/package/header/index.js.map +0 -1
  808. package/lib/module/package/image/index.js +0 -315
  809. package/lib/module/package/image/index.js.map +0 -1
  810. package/lib/module/package/radioButton/index.js +0 -66
  811. package/lib/module/package/radioButton/index.js.map +0 -1
  812. package/lib/module/package/safeAreaView/index.js +0 -21
  813. package/lib/module/package/safeAreaView/index.js.map +0 -1
  814. package/lib/module/package/scrollView/index.js +0 -18
  815. package/lib/module/package/scrollView/index.js.map +0 -1
  816. package/lib/module/package/separator/index.js +0 -26
  817. package/lib/module/package/separator/index.js.map +0 -1
  818. package/lib/module/package/spacer/index.js +0 -11
  819. package/lib/module/package/spacer/index.js.map +0 -1
  820. package/lib/module/package/spinner/index.js +0 -36
  821. package/lib/module/package/spinner/index.js.map +0 -1
  822. package/lib/module/package/stack/__test__/index.test.js +0 -89
  823. package/lib/module/package/stack/__test__/index.test.js.map +0 -1
  824. package/lib/module/package/stack/index.js +0 -49
  825. package/lib/module/package/stack/index.js.map +0 -1
  826. package/lib/module/package/styled/__test__/index.test.js +0 -158
  827. package/lib/module/package/styled/__test__/index.test.js.map +0 -1
  828. package/lib/module/package/styled/index.js +0 -35
  829. package/lib/module/package/styled/index.js.map +0 -1
  830. package/lib/module/package/switch/index.js +0 -36
  831. package/lib/module/package/switch/index.js.map +0 -1
  832. package/lib/module/package/text/index.js +0 -76
  833. package/lib/module/package/text/index.js.map +0 -1
  834. package/lib/module/package/theme.js.map +0 -1
  835. package/lib/module/package/utils/index.js +0 -120
  836. package/lib/module/package/utils/index.js.map +0 -1
  837. package/lib/module/package/utils/statusBar.js +0 -57
  838. package/lib/module/package/utils/statusBar.js.map +0 -1
  839. package/lib/module/package/utils/validator.js +0 -34
  840. package/lib/module/package/utils/validator.js.map +0 -1
  841. package/lib/module/styled.code-workspace +0 -9
  842. package/src/assets/img/blank_1.png +0 -0
  843. package/src/assets/img/blank_2.png +0 -0
  844. package/src/assets/img/doctor.png +0 -0
  845. package/src/index.js +0 -24
  846. package/src/package/badge/index.jsx +0 -135
  847. package/src/package/button/index.jsx +0 -83
  848. package/src/package/card/index.jsx +0 -52
  849. package/src/package/checkBox/index.jsx +0 -97
  850. package/src/package/cycle/index.jsx +0 -49
  851. package/src/package/dialog/index.jsx +0 -263
  852. package/src/package/dropdown/index.jsx +0 -101
  853. package/src/package/form/index.jsx +0 -144
  854. package/src/package/header/index.jsx +0 -96
  855. package/src/package/image/index.jsx +0 -300
  856. package/src/package/radioButton/index.jsx +0 -74
  857. package/src/package/safeAreaView/index.jsx +0 -20
  858. package/src/package/scrollView/index.jsx +0 -17
  859. package/src/package/separator/index.jsx +0 -27
  860. package/src/package/spacer/index.jsx +0 -13
  861. package/src/package/spinner/index.jsx +0 -33
  862. package/src/package/stack/__test__/index.test.js +0 -68
  863. package/src/package/stack/index.jsx +0 -50
  864. package/src/package/styled/__test__/index.test.js +0 -132
  865. package/src/package/styled/index.js +0 -32
  866. package/src/package/switch/index.jsx +0 -33
  867. package/src/package/text/index.jsx +0 -67
  868. package/src/package/theme.js +0 -377
  869. package/src/package/utils/index.js +0 -109
  870. package/src/package/utils/statusBar.js +0 -65
  871. package/src/package/utils/validator.js +0 -38
  872. package/src/styled.code-workspace +0 -9
package/README.md CHANGED
@@ -1,77 +1,4065 @@
1
- # fluent-styles
2
-
3
- Fluent-styles is a library designed to simplify the development of styled variants on UI components for both React and React Native projects. It enables developers to easily manage and apply base and variant styles to components dynamically based on props.
4
-
5
- ## Features
6
-
7
- - **Dynamic Styling**: Easily apply and switch styles based on the props of the components.
8
- - **Support for React and React Native**: Can be used seamlessly across web and mobile platforms.
9
-
10
- ## Installation
11
-
12
- Install fluent-styles using npm:
13
-
14
- ```bash
15
- npm install fluent-styles
16
-
17
- Or using yarn:
18
-
19
- yarn add fluent-styles
20
-
21
- Usage
22
- After installing the package, you can use it to wrap your components with styled functionality. Below is a basic example of how to use fluent-styles in a React application:
23
-
24
- React Example
25
-
26
- import React from 'react';
27
- import styled from 'fluent-styles';
28
-
29
- const baseStyles = {
30
- padding: '20px',
31
- color: 'white'
32
- };
33
-
34
- const variants = {
35
- mood: {
36
- happy: { backgroundColor: 'yellow' },
37
- sad: { backgroundColor: 'blue' }
38
- }
39
- };
40
-
41
- const MoodText = styled('div', { base: baseStyles, variants });
42
-
43
- function App() {
44
- return (
45
- <MoodText mood="happy">I am happy!</MoodText>
46
- );
47
- }
48
-
49
- export default App;
50
-
51
- React Native Example
52
- import React from 'react';
53
- import { Text } from 'react-native';
54
- import styled from 'style-ease';
55
-
56
- const baseStyles = {
57
- padding: 10,
58
- color: 'white'
59
- };
60
-
61
- const variants = {
62
- mood: {
63
- happy: { backgroundColor: 'yellow' },
64
- sad: { backgroundColor: 'blue' }
65
- }
66
- };
67
-
68
- const MoodText = styled(Text, { base: baseStyles, variants });
69
-
70
- function App() {
71
- return (
72
- <MoodText mood="happy">I am happy!</MoodText>
73
- );
74
- }
75
-
76
- export default App;
77
-
1
+ # Fluent Styles
2
+
3
+ A comprehensive, TypeScript-first React Native UI library providing production-ready components, hooks, and an imperative service layer all powered by a portal-based rendering system.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/fluent-styles)](https://www.npmjs.com/package/fluent-styles)
6
+ [![License: Apache-2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
7
+
8
+ ---
9
+
10
+ ## Table of Contents
11
+
12
+ - [Installation](#installation)
13
+ - [Quick Start](#quick-start)
14
+ - [Portal System](#portal-system)
15
+ - [Components](#components)
16
+ - [StyledButton](#styledbutton)
17
+ - [StyledTextInput](#styledtextinput)
18
+ - [Switch](#switch)
19
+ - [StyledCheckBox](#styledcheckbox)
20
+ - [StyledCard](#styledcard)
21
+ - [StyledBadge / BadgeWithIcon](#styledbadge--badgewithicon)
22
+ - [StyledImage / StyledImageBackground](#styledimage--styledimagebackground)
23
+ - [StyledHeader / FullHeader](#styledheader--fullheader)
24
+ - [StyledDropdown / StyledMultiSelectDropdown](#styleddropdown--styledmultiselectdropdown)
25
+ - [Popup](#popup)
26
+ - [Drawer](#drawer)
27
+ - [Collapse / CollapseGroup](#collapse--collapsegroup)
28
+ - [TabBar](#tabbar)
29
+ - [StyledDivider](#styleddivider)
30
+ - [StyledSeperator](#styledseperator)
31
+ - [Stack](#stack)
32
+ - [StyledText](#styledtext)
33
+ - [StyledPressable](#styledpressable)
34
+ - [StyledPage / StyledScrollView](#styledpage--styledscrollview)
35
+ - [StyledSafeAreaView](#styledsafeareaview)
36
+ - [Spacer](#spacer)
37
+ - [StyledShape](#styledshape)
38
+ - [Loader](#loader)
39
+ - [StyledCircularProgress](#styledcircularprogress)
40
+ - [StyledChip](#styledchip)
41
+ - [StyledBar](#styledbar)
42
+ - [StyledTimeline](#styledtimeline)
43
+ - [StyledRadio / StyledRadioGroup](#styledradio--styledradiogroup)
44
+ - [StyledProgressBar](#styledprogressbar)
45
+ - [StyledSlider](#styledslider)
46
+ - [StyledDatePicker](#styleddatepicker)
47
+ - [StyledBottomSheet](#styledbottomsheet)
48
+ - [StyledEmptyState](#styledemptystate)
49
+ - [StyledSearchBar](#styledsearchbar)
50
+ - [StyledSkeleton](#styledskeleton)
51
+ - [Hooks](#hooks)
52
+ - [useToast](#usetoast)
53
+ - [useNotification](#usenotification)
54
+ - [useDialogue](#usedialogue)
55
+ - [useActionSheet](#useactionsheet)
56
+ - [useLoader](#useloader)
57
+ - [Imperative Services](#imperative-services)
58
+ - [toastService](#toastservice)
59
+ - [notificationService](#notificationservice)
60
+ - [dialogueService](#dialogueservice)
61
+ - [actionSheetService](#actionsheetservice)
62
+ - [loaderService](#loaderservice)
63
+ - [Theme & Tokens](#theme--tokens)
64
+ - [Contributing](#contributing)
65
+ - [License](#license)
66
+
67
+ ---
68
+
69
+ ## Installation
70
+
71
+ ```bash
72
+ npm install fluent-styles
73
+ # or
74
+ yarn add fluent-styles
75
+ ```
76
+
77
+ Peer dependencies (install separately if not already present):
78
+
79
+ ```bash
80
+ npm install react-native-safe-area-context
81
+ ```
82
+
83
+ ---
84
+
85
+ ## Quick Start
86
+
87
+ Wrap your root component with `GlobalPortalProvider`. This single wrapper enables **all** portal-based UI — toasts, notifications, loaders, dialogues, drawers, and action sheets.
88
+
89
+ ```tsx
90
+ import { GlobalPortalProvider } from 'fluent-styles'
91
+
92
+ export default function App() {
93
+ return (
94
+ <GlobalPortalProvider>
95
+ <YourNavigator />
96
+ </GlobalPortalProvider>
97
+ )
98
+ }
99
+ ```
100
+
101
+ If you also use the declarative hooks (`useToast`, `useDialogue`, etc.) inside descendant components, nest a `PortalManager` as well:
102
+
103
+ ```tsx
104
+ import { GlobalPortalProvider, PortalManager } from 'fluent-styles'
105
+
106
+ export default function App() {
107
+ return (
108
+ <GlobalPortalProvider>
109
+ <PortalManager>
110
+ <YourNavigator />
111
+ </PortalManager>
112
+ </GlobalPortalProvider>
113
+ )
114
+ }
115
+ ```
116
+
117
+ ---
118
+
119
+ ## Portal System
120
+
121
+ Fluent Styles uses a dual-layer portal architecture:
122
+
123
+ | Layer | Provider | Use case |
124
+ |---|---|---|
125
+ | **Global singleton** | `GlobalPortalProvider` | Imperative services (`toastService`, `loaderService`, etc.) — usable outside React |
126
+ | **Declarative hooks** | `PortalManager` | Hook APIs (`useToast`, `useDialogue`, etc.) — usable inside React components |
127
+
128
+ The low-level `portal` singleton is also exported for advanced use:
129
+
130
+ ```ts
131
+ import { portal } from 'fluent-styles'
132
+
133
+ const id = portal.show(<MyWidget />, { position: 'top', backdrop: false })
134
+ setTimeout(() => portal.hide(id), 3000)
135
+ ```
136
+
137
+ ---
138
+
139
+ ## Components
140
+
141
+ ### StyledButton
142
+
143
+ A fully themed button with variant, shape, size, and icon support.
144
+
145
+ ```tsx
146
+ import { StyledButton, theme } from 'fluent-styles'
147
+
148
+ // Variants (compact style)
149
+ <StyledButton primary compact>
150
+ <StyledButton.Text color={theme.colors.white} fontSize={theme.fontSize.small} fontWeight={theme.fontWeight.semiBold}>
151
+ Primary
152
+ </StyledButton.Text>
153
+ </StyledButton>
154
+ <StyledButton secondary compact>Secondary</StyledButton>
155
+ <StyledButton outline compact>Outline</StyledButton>
156
+ <StyledButton ghost compact>Ghost</StyledButton>
157
+ <StyledButton link compact>Link</StyledButton>
158
+ <StyledButton danger compact>Danger</StyledButton>
159
+ <StyledButton success compact>Success</StyledButton>
160
+ <StyledButton warning compact>Warning</StyledButton>
161
+ <StyledButton disabled compact>Disabled</StyledButton>
162
+
163
+ // Sizes: xs | sm | md | lg | xl
164
+ {(['xs', 'sm', 'md', 'lg', 'xl'] as const).map((s) => (
165
+ <StyledButton key={s} primary compact {...{ [s]: true }}>
166
+ <StyledButton.Text color={theme.colors.white}>{s.toUpperCase()}</StyledButton.Text>
167
+ </StyledButton>
168
+ ))}
169
+
170
+ // Shapes
171
+ <StyledButton primary compact pill>Pill</StyledButton>
172
+ <StyledButton primary compact rounded>Rounded</StyledButton>
173
+ <StyledButton backgroundColor={theme.colors.yellow[500]} borderWidth={0} square>Square</StyledButton>
174
+
175
+ // Icons — left, right, or both
176
+ <StyledButton primary compact leftIcon={<Icon emoji="🚀" />}>Deploy</StyledButton>
177
+ <StyledButton outline compact rightIcon={<Icon emoji="→" />}>Continue</StyledButton>
178
+ <StyledButton secondary compact leftIcon={<Icon emoji="⬇" />} rightIcon={<Icon emoji="📦" />}>
179
+ Download package
180
+ </StyledButton>
181
+
182
+ // Icon-only circular buttons
183
+ <StyledButton icon backgroundColor={theme.colors.indigo[500]}><Icon emoji="✉️" size={18} /></StyledButton>
184
+ <StyledButton icon backgroundColor={theme.colors.amber[400]}><Icon emoji="🔔" size={18} /></StyledButton>
185
+ <StyledButton icon backgroundColor={theme.colors.red[500]}><Icon emoji="🗑️" size={18} /></StyledButton>
186
+
187
+ // Loading state (async example)
188
+ const [loading, setLoading] = useState(false)
189
+ <StyledButton primary compact loading={loading} onPress={() => { setLoading(true); setTimeout(() => setLoading(false), 2000) }}>
190
+ <StyledButton.Text color={theme.colors.white}>{loading ? 'Saving…' : 'Save changes'}</StyledButton.Text>
191
+ </StyledButton>
192
+
193
+ // Full-width block
194
+ <StyledButton primary block>
195
+ <StyledButton.Text color={theme.colors.white} fontSize={theme.fontSize.medium} fontWeight={theme.fontWeight.bold}>
196
+ Create account
197
+ </StyledButton.Text>
198
+ </StyledButton>
199
+ <StyledButton outline block>
200
+ <StyledButton.Text color={theme.colors.gray[800]} fontSize={theme.fontSize.medium}>Sign in instead</StyledButton.Text>
201
+ </StyledButton>
202
+
203
+ // Disabled
204
+ <StyledButton primary disabled>Disabled</StyledButton>
205
+ ```
206
+
207
+ Accepts all `TouchableOpacityProps` and flat `ViewStyle` props.
208
+
209
+ ---
210
+
211
+ ### StyledTextInput
212
+
213
+ A rich text input with label, validation, addons, and an imperative ref handle.
214
+
215
+ ```tsx
216
+ import { StyledTextInput } from 'fluent-styles'
217
+
218
+ // Variants: outline | filled | underline | ghost
219
+ <StyledTextInput variant="outline" label="Email" placeholder="you@example.com" />
220
+
221
+ // Sizes: sm | md | lg
222
+ <StyledTextInput size="md" label="Medium" />
223
+
224
+ // Floating label (Material Design style)
225
+ <StyledTextInput floatLabel label="Floating Label" />
226
+
227
+ // Validation
228
+ <StyledTextInput
229
+ label="Username"
230
+ required
231
+ helperText="Must be unique"
232
+ errorMessage="Already taken"
233
+ error
234
+ />
235
+
236
+ // Character counter
237
+ <StyledTextInput label="Bio" showCounter maxLength={200} multiline />
238
+
239
+ // Icons & addons
240
+ <StyledTextInput leftIcon={<SearchIcon />} placeholder="Search…" />
241
+ <StyledTextInput
242
+ leftAddon={{ text: 'https://', bg: '#f4f4f5' }}
243
+ rightAddon={{ text: '.com', bg: '#f4f4f5' }}
244
+ placeholder="yoursite"
245
+ />
246
+
247
+ // States
248
+ <StyledTextInput clearable value={value} onChangeText={setValue} />
249
+ <StyledTextInput loading />
250
+
251
+ // Imperative handle
252
+ const inputRef = useRef<StyledTextInputHandle>(null)
253
+ <StyledTextInput ref={inputRef} label="Password" secureTextEntry />
254
+ inputRef.current?.focus()
255
+ inputRef.current?.clear()
256
+ inputRef.current?.isFocused()
257
+ ```
258
+
259
+ **Key props:**
260
+
261
+ | Prop | Type | Description |
262
+ |---|---|---|
263
+ | `label` | `string` | Input label |
264
+ | `floatLabel` | `boolean` | Animated floating label |
265
+ | `required` | `boolean` | Appends `*` to label |
266
+ | `helperText` | `string` | Hint text below input |
267
+ | `errorMessage` | `string` | Error text (visible when `error` is true) |
268
+ | `error` | `boolean` | Activates error state |
269
+ | `showCounter` | `boolean` | Character counter (requires `maxLength`) |
270
+ | `variant` | `outline \| filled \| underline \| ghost` | Visual style |
271
+ | `size` | `sm \| md \| lg` | Input size |
272
+ | `leftIcon / rightIcon` | `ReactNode` | Icon slots |
273
+ | `leftAddon / rightAddon` | `{ text?, node?, bg?, color?, onPress? }` | Prefix/suffix addons |
274
+ | `clearable` | `boolean` | Clear button when value is present |
275
+ | `loading` | `boolean` | Right-side activity spinner |
276
+ | `focusColor` | `string` | Border colour on focus |
277
+
278
+ ---
279
+
280
+ ### Switch
281
+
282
+ A generic animated toggle with async confirmation guard and customisable labels.
283
+
284
+ ```tsx
285
+ import { Switch } from 'fluent-styles'
286
+
287
+ // Uncontrolled boolean
288
+ <Switch defaultValue={false} onChange={(val) => console.log(val)} />
289
+
290
+ // Controlled
291
+ <Switch value={isOn} onChange={setIsOn} />
292
+
293
+ // Non-boolean (generic types)
294
+ <Switch<'yes', 'no'>
295
+ activeValue="yes"
296
+ inactiveValue="no"
297
+ defaultValue="no"
298
+ onChange={(val) => console.log(val)}
299
+ />
300
+
301
+ // Sizes: sm | md | lg
302
+ <Switch size="lg" defaultValue />
303
+
304
+ // Async guard — return false to cancel toggle
305
+ <Switch
306
+ beforeChange={async (next) => {
307
+ const ok = await confirmDialog()
308
+ return ok
309
+ }}
310
+ />
311
+
312
+ // Inline labels & colours
313
+ <Switch activeLabel="ON" inactiveLabel="OFF" />
314
+ <Switch activeColor="#10b981" inactiveColor="#d4d4d8" />
315
+
316
+ // Fine-grained color token overrides
317
+ <Switch
318
+ defaultValue
319
+ colors={{
320
+ activeTrack: palettes.rose[500],
321
+ inactiveTrack: palettes.rose[100],
322
+ inactiveBorder: palettes.rose[200],
323
+ activeLabelText: '#fff',
324
+ }}
325
+ />
326
+ <Switch defaultValue activeColor={palettes.amber[400]} inactiveColor={palettes.amber[100]} />
327
+
328
+ // Teal with labels
329
+ <Switch
330
+ size="lg"
331
+ defaultValue
332
+ activeLabel="ON"
333
+ inactiveLabel="OFF"
334
+ colors={{
335
+ activeTrack: palettes.teal[500],
336
+ inactiveTrack: palettes.teal[100],
337
+ activeLabelText: '#fff',
338
+ inactiveLabelText: palettes.teal[400],
339
+ }}
340
+ />
341
+
342
+ // On a dark background (slate palette)
343
+ <Switch
344
+ defaultValue
345
+ colors={{
346
+ activeTrack: palettes.indigo[400],
347
+ inactiveTrack: palettes.blueGray[700],
348
+ inactiveBorder: palettes.blueGray[600],
349
+ thumb: '#ffffff',
350
+ }}
351
+ />
352
+
353
+ // Always-rejected guard (demonstrates async guard returning false)
354
+ <Switch defaultValue={false} beforeChange={() => Promise.resolve(false)} />
355
+
356
+ // States
357
+ <Switch loading />
358
+ <Switch disabled />
359
+ ```
360
+
361
+ **Key props:**
362
+
363
+ | Prop | Type | Description |
364
+ |---|---|---|
365
+ | `value` | `T` | Controlled value |
366
+ | `defaultValue` | `T` | Uncontrolled initial value |
367
+ | `activeValue / inactiveValue` | `T` | Values for on/off (default `true`/`false`) |
368
+ | `onChange` | `(val: T) => void` | Change callback |
369
+ | `beforeChange` | `(next: T) => boolean \| Promise<boolean>` | Async guard |
370
+ | `size` | `sm \| md \| lg` | Track size preset |
371
+ | `activeLabel / inactiveLabel` | `string \| ReactNode` | Label inside track |
372
+ | `activeColor / inactiveColor` | `string` | Track colour overrides |
373
+ | `loading` | `boolean` | Replaces thumb with a spinner |
374
+ | `disabled` | `boolean` | Disables interaction |
375
+ | `colors` | `Partial<SwitchColors>` | Fine-grained token overrides |
376
+
377
+ ---
378
+
379
+ ### StyledCheckBox
380
+
381
+ An accessible checkbox with customisable size and colour.
382
+
383
+ ```tsx
384
+ import { StyledCheckBox, StyledCard, StyledText, Stack, theme } from 'fluent-styles'
385
+
386
+ // Basic
387
+ <StyledCheckBox checked={isChecked} onCheck={setChecked} />
388
+
389
+ // Sizes: 18 | 24 | 32 | 40
390
+ <Stack horizontal gap={16} alignItems="center">
391
+ <StyledCheckBox checked size={18} onCheck={() => {}} />
392
+ <StyledCheckBox checked size={24} onCheck={() => {}} />
393
+ <StyledCheckBox checked size={32} onCheck={() => {}} />
394
+ <StyledCheckBox checked size={40} onCheck={() => {}} />
395
+ </Stack>
396
+
397
+ // Custom colors
398
+ <StyledCheckBox checked checkedColor={theme.colors.green[500]} checkMarkColor="#fff" onCheck={() => {}} />
399
+ <StyledCheckBox checked checkedColor={theme.colors.blue[600]} checkMarkColor="#fff" onCheck={() => {}} />
400
+ <StyledCheckBox checked checkedColor={theme.colors.rose[500]} checkMarkColor="#fff" onCheck={() => {}} />
401
+
402
+ // Disabled states
403
+ <StyledCheckBox checked={false} disabled onCheck={() => {}} />
404
+ <StyledCheckBox checked disabled onCheck={() => {}} />
405
+
406
+ // --- Real-world: Settings preferences card ---
407
+ <StyledCard backgroundColor={theme.colors.white} borderRadius={18} padding={16} shadow="light">
408
+ <Stack gap={18}>
409
+ <StyledText fontSize={18} fontWeight={800}>Preferences</StyledText>
410
+ {[{ label: 'Product updates', checked: updates, setter: setUpdates },
411
+ { label: 'Marketing emails', checked: marketing, setter: setMarketing },
412
+ { label: 'Push notifications', checked: notifs, setter: setNotifs }]
413
+ .map(({ label, checked, setter }) => (
414
+ <Stack key={label} horizontal alignItems="center" gap={12}>
415
+ <StyledCheckBox checked={checked} onCheck={setter} />
416
+ <StyledText fontSize={15} fontWeight={600}>{label}</StyledText>
417
+ </Stack>
418
+ ))}
419
+ </Stack>
420
+ </StyledCard>
421
+
422
+ // --- Real-world: Task list with green checkmarks ---
423
+ <Stack gap={16}>
424
+ {tasks.map(({ key, label, helper }) => (
425
+ <Stack key={key} horizontal alignItems="center" gap={12}>
426
+ <StyledCheckBox
427
+ checked={done[key]}
428
+ onCheck={(v) => setDone(prev => ({ ...prev, [key]: v }))}
429
+ checkedColor={theme.colors.green[500]}
430
+ checkMarkColor="#fff"
431
+ />
432
+ <Stack flex={1}>
433
+ <StyledText fontSize={15} fontWeight={600}>{label}</StyledText>
434
+ {helper && <StyledText fontSize={13} color={theme.colors.gray[500]}>{helper}</StyledText>}
435
+ </Stack>
436
+ </Stack>
437
+ ))}
438
+ </Stack>
439
+
440
+ // --- Compact inline usage ---
441
+ <Stack horizontal alignItems="center" gap={10}>
442
+ <StyledCheckBox checked={remember} onCheck={setRemember} size={20} />
443
+ <StyledText>Remember me</StyledText>
444
+ </Stack>
445
+ ```
446
+
447
+ ---
448
+
449
+ ### StyledCard
450
+
451
+ A flexible container with optional shadow levels and pressable wrapper.
452
+
453
+ ```tsx
454
+ import { StyledCard } from 'fluent-styles'
455
+
456
+ <StyledCard shadow="light" padding={16} borderRadius={12}>
457
+ <StyledText>Card content</StyledText>
458
+ </StyledCard>
459
+
460
+ // Pressable card
461
+ <StyledCard
462
+ shadow="medium"
463
+ pressable
464
+ pressableProps={{ onPress: () => navigate('Detail') }}
465
+ >
466
+ <StyledText>Tap me</StyledText>
467
+ </StyledCard>
468
+ ```
469
+
470
+ **Shadow levels:** `light` | `lightMedium` | `medium` | `mediumDark` | `dark` | `veryDark`
471
+
472
+ Accepts all `ViewProps` and flat `ViewStyle` props.
473
+
474
+ ---
475
+
476
+ ### StyledBadge / BadgeWithIcon / BadgeIcon
477
+
478
+ Styled text badges, icon badges, and notification count overlays.
479
+
480
+ ```tsx
481
+ import { StyledBadge, BadgeWithIcon, BadgeIcon, StyledImage, Stack, theme } from 'fluent-styles'
482
+
483
+ // --- Pill status badges ---
484
+ <Stack horizontal gap={10} flexWrap="wrap">
485
+ <StyledBadge
486
+ backgroundColor={theme.colors.green[50]}
487
+ color={theme.colors.green[700]}
488
+ paddingHorizontal={10} paddingVertical={6} borderRadius={999}
489
+ >Active</StyledBadge>
490
+
491
+ <StyledBadge
492
+ backgroundColor={theme.colors.red[50]}
493
+ color={theme.colors.red[600]}
494
+ paddingHorizontal={10} paddingVertical={6} borderRadius={999}
495
+ >Rejected</StyledBadge>
496
+
497
+ <StyledBadge
498
+ backgroundColor={theme.colors.blue[50]}
499
+ color={theme.colors.blue[700]}
500
+ paddingHorizontal={10} paddingVertical={6} borderRadius={999}
501
+ >New</StyledBadge>
502
+ </Stack>
503
+
504
+ // --- Link badge ---
505
+ <StyledBadge link>View details</StyledBadge>
506
+
507
+ // --- BadgeWithIcon: feature / status badges ---
508
+ <BadgeWithIcon
509
+ title="Featured"
510
+ iconLeft={<Text>⭐</Text>}
511
+ backgroundColor={theme.colors.yellow[50]}
512
+ paddingHorizontal={12} paddingVertical={7} borderRadius={999} gap={6}
513
+ />
514
+ <BadgeWithIcon
515
+ title="Verified"
516
+ iconLeft={<Text>✅</Text>}
517
+ backgroundColor={theme.colors.green[50]}
518
+ paddingHorizontal={12} paddingVertical={7} borderRadius={999} gap={6}
519
+ />
520
+
521
+ // --- Status badges (workflow states) ---
522
+ <BadgeWithIcon title="In progress" iconLeft={<Text>🟡</Text>} color={theme.colors.yellow[700]}
523
+ backgroundColor={theme.colors.yellow[50]} paddingHorizontal={12} paddingVertical={8} borderRadius={999} gap={6} />
524
+ <BadgeWithIcon title="Completed" iconLeft={<Text>🟢</Text>} color={theme.colors.green[700]}
525
+ backgroundColor={theme.colors.green[50]} paddingHorizontal={12} paddingVertical={8} borderRadius={999} gap={6} />
526
+ <BadgeWithIcon title="Blocked" iconLeft={<Text>🔴</Text>} color={theme.colors.red[700]}
527
+ backgroundColor={theme.colors.red[50]} paddingHorizontal={12} paddingVertical={8} borderRadius={999} gap={6} />
528
+
529
+ // --- BadgeIcon: count bubbles ---
530
+ <Stack horizontal gap={24} alignItems="center">
531
+ <BadgeIcon char="1" size={24} />
532
+ <BadgeIcon char="3" backgroundColor={theme.colors.blue[600]} size={24} />
533
+ <BadgeIcon char="9+" backgroundColor={theme.colors.gray[800]} size={24} />
534
+ </Stack>
535
+
536
+ // --- BadgeIcon over an icon (notification dot) ---
537
+ <BadgeIcon icon={<Text style={{ fontSize: 24 }}>🔔</Text>} char="2" right={20} top={-12} size={16} />
538
+ <BadgeIcon icon={<Text style={{ fontSize: 24 }}>🛒</Text>} char="4" backgroundColor={theme.colors.green[600]} right={16} top={-12} size={16} />
539
+
540
+ // --- Overlay badge on an image ---
541
+ <Stack>
542
+ <StyledImage source={{ uri: '…' }} width={220} height={150} borderRadius={18} />
543
+ <Stack position="absolute" top={10} right={10}>
544
+ <StyledBadge backgroundColor="rgba(17,24,39,0.78)" color="#fff"
545
+ paddingHorizontal={10} paddingVertical={6} borderRadius={999} fontWeight="700">New</StyledBadge>
546
+ </Stack>
547
+ </Stack>
548
+
549
+ // --- Avatar with notification count ---
550
+ <Stack>
551
+ <StyledImage source={{ uri: '…' }} cycle size={64} borderRadius={999} />
552
+ <Stack position="absolute" top={2} right={2}>
553
+ <BadgeIcon char="3" size={18} />
554
+ </Stack>
555
+ </Stack>
556
+
557
+ // --- Product badges ---
558
+ <Stack horizontal gap={10} flexWrap="wrap">
559
+ <StyledBadge backgroundColor={theme.colors.red[50]} color={theme.colors.red[600]}
560
+ paddingHorizontal={10} paddingVertical={6} borderRadius={999} fontWeight="700">Sale</StyledBadge>
561
+ <StyledBadge backgroundColor={theme.colors.gray[900]} color={theme.colors.white}
562
+ paddingHorizontal={10} paddingVertical={6} borderRadius={999} fontWeight="700">Limited</StyledBadge>
563
+ <BadgeWithIcon title="Free delivery" iconLeft={<Text>🚚</Text>} backgroundColor={theme.colors.green[50]}
564
+ paddingHorizontal={12} paddingVertical={7} borderRadius={999} gap={6} />
565
+ </Stack>
566
+ ```
567
+
568
+ **`BadgeIcon` props:** `char`, `icon?`, `size?`, `backgroundColor?`, `top?`, `right?`
569
+
570
+ ---
571
+
572
+ ### StyledImage / StyledImageBackground
573
+
574
+ Styled wrappers around React Native's `Image` and `ImageBackground`.
575
+
576
+ ```tsx
577
+ import { StyledImage, StyledImageBackground } from 'fluent-styles'
578
+
579
+ // Fixed dimensions
580
+ <StyledImage source={{ uri: 'https://…' }} width={120} height={80} borderRadius={8} />
581
+
582
+ // Circular avatar (cycle + size)
583
+ <StyledImage source={{ uri: 'https://…' }} cycle size={64} />
584
+
585
+ // Background image with overlay
586
+ <StyledImageBackground source={require('./assets/hero.jpg')} height={200} borderRadius={12}>
587
+ <StyledText color="#fff">Overlay text</StyledText>
588
+ </StyledImageBackground>
589
+ ```
590
+
591
+ ---
592
+
593
+ ### StyledHeader / FullHeader
594
+
595
+ A pre-built navigation header with optional status bar management.
596
+
597
+ ```tsx
598
+ import { StyledHeader, FullHeader } from 'fluent-styles'
599
+
600
+ <StyledHeader
601
+ title="Settings"
602
+ titleAlignment="center"
603
+ showBackArrow
604
+ onBackPress={() => navigation.goBack()}
605
+ rightIcon={<SettingsIcon />}
606
+ />
607
+
608
+ // Custom back button styling
609
+ <StyledHeader
610
+ title="Profile"
611
+ showBackArrow
612
+ backArrowProps={{ size: 20, color: '#6366f1', onPress: () => navigation.goBack() }}
613
+ />
614
+
615
+ // Full header — manages status bar spacer automatically
616
+ <FullHeader statusBarProps={{ barStyle: 'dark-content' }}>
617
+ <MyCustomHeaderContent />
618
+ </FullHeader>
619
+ ```
620
+
621
+ | Prop | Type | Description |
622
+ |---|---|---|
623
+ | `title` | `string` | Header title |
624
+ | `titleAlignment` | `left \| center \| right` | Title alignment |
625
+ | `showBackArrow` | `boolean` | Renders a back arrow |
626
+ | `onBackPress` | `() => void` | Back arrow handler |
627
+ | `leftIcon / rightIcon` | `ReactNode` | Custom icon slots |
628
+ | `backArrowProps` | `BackArrowProps` | Size, colour, custom press handler |
629
+ | `showStatusBar` | `boolean` | Include status bar spacer |
630
+
631
+ ---
632
+
633
+ ### StyledDropdown / StyledMultiSelectDropdown
634
+
635
+ Modal-based dropdowns with optional search, icons, and subtitles.
636
+
637
+ ```tsx
638
+ import { StyledDropdown, StyledMultiSelectDropdown } from 'fluent-styles'
639
+
640
+ const options = [
641
+ { value: 'react', label: 'React Native' },
642
+ { value: 'flutter', label: 'Flutter', subtitle: 'by Google' },
643
+ { value: 'ionic', label: 'Ionic', disabled: true },
644
+ ]
645
+
646
+ // Single-select
647
+ <StyledDropdown
648
+ label="Framework"
649
+ placeholder="Pick one…"
650
+ options={options}
651
+ value={selected}
652
+ onChange={(item) => setSelected(item.value)}
653
+ variant="outline"
654
+ size="md"
655
+ />
656
+
657
+ // With search
658
+ <StyledDropdown label="Country" options={countryOptions} searchable searchPlaceholder="Filter…" />
659
+
660
+ // Multi-select
661
+ <StyledMultiSelectDropdown label="Tags" options={options} value={selectedTags} onChange={setSelectedTags} />
662
+ ```
663
+
664
+ **DropdownOptionItem:** `value`, `label`, `icon?`, `subtitle?`, `disabled?`, `meta?`
665
+
666
+ **Trigger props:** `variant` (`outline | filled | underline | ghost`), `size` (`sm | md | lg`), `label`, `placeholder`, `disabled`
667
+
668
+ ---
669
+
670
+ ### Popup
671
+
672
+ A versatile overlay with multiple positions, animation styles, and a built-in header.
673
+
674
+ ```tsx
675
+ import { Popup } from 'fluent-styles'
676
+
677
+ // --- Bottom sheet variants ---
678
+ <Popup visible={visible} onClose={hide}>Plain content — no header</Popup>
679
+ <Popup visible={visible} onClose={hide} title="Post options" showClose><ActionList /></Popup>
680
+ <Popup visible={visible} onClose={hide} title="Share post" subtitle="Choose where to send" showClose><ShareList /></Popup>
681
+ <Popup visible={visible} onClose={hide} title="Safe area" showClose safeAreaBottom>…</Popup>
682
+
683
+ // No backdrop
684
+ <Popup visible={visible} onClose={hide} title="No backdrop" overlay={false} showClose>…</Popup>
685
+
686
+ // Prevent dismiss on backdrop tap
687
+ <Popup visible={visible} onClose={hide} title="Tap overlay — nothing" showClose closeOnPressOverlay={false}>…</Popup>
688
+
689
+ // --- Positions ---
690
+ <Popup visible={visible} onClose={hide} position="top" title="Notification" showClose>…</Popup>
691
+ <Popup visible={visible} onClose={hide} position="left" title="Side menu" showClose>…</Popup>
692
+ <Popup visible={visible} onClose={hide} position="right" title="Filters" showClose>…</Popup>
693
+ <Popup visible={visible} onClose={hide} position="center" title="Confirm" showClose round>…</Popup>
694
+
695
+ // --- Animation styles ---
696
+ <Popup visible={visible} onClose={hide} animation="slide" title="Slide" showClose>…</Popup>
697
+ <Popup visible={visible} onClose={hide} animation="fade" title="Fade" showClose>…</Popup>
698
+ <Popup visible={visible} onClose={hide} position="center" animation="scale" title="Scale" showClose round>…</Popup>
699
+ <Popup visible={visible} onClose={hide} animation="none" title="Instant" showClose>…</Popup>
700
+
701
+ // Spring physics
702
+ <Popup visible={visible} onClose={hide} title="Spring" showClose spring={{ damping: 18, stiffness: 280 }}>…</Popup>
703
+
704
+ // --- Corner rounding ---
705
+ <Popup visible={visible} onClose={hide} round title="Default 20 px" showClose>…</Popup>
706
+ <Popup visible={visible} onClose={hide} round={false} title="Square corners" showClose>…</Popup>
707
+ <Popup visible={visible} onClose={hide} round roundRadius={36} title="Large radius" showClose>…</Popup>
708
+
709
+ // --- Render strategy ---
710
+ <Popup visible={visible} onClose={hide} lazyRender title="Lazy (default)" showClose>…</Popup>
711
+ <Popup visible={visible} onClose={hide} destroyOnClose title="Destroy on close" showClose>…</Popup>
712
+
713
+ // --- Color token overrides ---
714
+ <Popup
715
+ visible={visible} onClose={hide}
716
+ title="Dark slate" subtitle="Token overrides" showClose
717
+ colors={{
718
+ background: palettes.blueGray[900],
719
+ overlay: 'rgba(0,0,0,0.75)',
720
+ handle: palettes.blueGray[600],
721
+ headerTitle: palettes.blueGray[100],
722
+ headerSubtitle: palettes.blueGray[400],
723
+ headerBorder: palettes.blueGray[700],
724
+ closeIcon: palettes.blueGray[300],
725
+ closeIconBg: palettes.blueGray[700],
726
+ }}
727
+ >…</Popup>
728
+
729
+ <Popup
730
+ visible={visible} onClose={hide} title="Indigo surface" showClose
731
+ colors={{ background: palettes.indigo[50], headerTitle: palettes.indigo[900], handle: palettes.indigo[300], closeIconBg: palettes.indigo[100] }}
732
+ >…</Popup>
733
+
734
+ // --- Lifecycle callbacks ---
735
+ <Popup
736
+ visible={visible} onClose={hide} title="Lifecycle" showClose
737
+ onOpen={() => console.log('onOpen')}
738
+ onOpened={() => console.log('onOpened — animation done')}
739
+ onClosed={() => console.log('onClosed — animation done')}
740
+ >…</Popup>
741
+ ```
742
+
743
+ | Prop | Type | Default | Description |
744
+ |---|---|---|---|
745
+ | `visible` | `boolean` | — | Controls visibility |
746
+ | `position` | `top \| bottom \| left \| right \| center` | `bottom` | Entry edge |
747
+ | `animation` | `slide \| fade \| scale \| none` | auto | Animation style |
748
+ | `overlay` | `boolean` | `true` | Show backdrop |
749
+ | `closeOnPressOverlay` | `boolean` | `true` | Dismiss on backdrop tap |
750
+ | `round / roundRadius` | `boolean / number` | `true / 20` | Rounded interior corners |
751
+ | `title / subtitle` | `ReactNode` | — | Built-in header content |
752
+ | `showHandle` | `boolean` | auto | Drag handle pill |
753
+ | `showClose` | `boolean` | `false` | Close (×) button |
754
+ | `safeAreaBottom` | `boolean` | `false` | Padding for home bar |
755
+ | `lazyRender` | `boolean` | `true` | Mount children on first open |
756
+ | `destroyOnClose` | `boolean` | `false` | Unmount children when closed |
757
+ | `spring` | `{ damping, stiffness, mass? }` | — | Spring physics override |
758
+ | `colors` | `Partial<PopupColors>` | — | Token overrides |
759
+
760
+ **Lifecycle callbacks:** `onOpen`, `onOpened`, `onClose`, `onClosed`
761
+
762
+ ---
763
+
764
+ ### Drawer
765
+
766
+ A swipeable side panel with built-in navigation list, header, and footer slot.
767
+
768
+ ```tsx
769
+ import { Drawer } from 'fluent-styles'
770
+
771
+ <Drawer
772
+ visible={isOpen}
773
+ side="left"
774
+ title="Menu"
775
+ onClose={() => setOpen(false)}
776
+ navItems={[
777
+ { key: 'home', label: 'Home', icon: '🏠', active: true, onPress: () => navigate('Home') },
778
+ { key: 'profile', label: 'Profile', icon: '👤', onPress: () => navigate('Profile') },
779
+ { key: 'logout', label: 'Logout', icon: '🚪', section: 'Account', onPress: logout },
780
+ ]}
781
+ footer={<UserProfileRow />}
782
+ />
783
+ ```
784
+
785
+ | Prop | Type | Default | Description |
786
+ |---|---|---|---|
787
+ | `visible` | `boolean` | — | Controls visibility |
788
+ | `side` | `left \| right` | `left` | Entry edge |
789
+ | `width` | `number \| string` | `'78%'` | Drawer width |
790
+ | `navItems` | `DrawerNavItem[]` | — | Built-in nav list (auto-grouped by `section`) |
791
+ | `footer` | `ReactNode` | — | Pinned footer content |
792
+ | `swipeToClose` | `boolean` | `true` | Pan gesture dismiss |
793
+ | `swipeThreshold` | `number` | `0.4` | Fraction of width to trigger dismiss |
794
+ | `title / subtitle` | `ReactNode` | — | Header content |
795
+ | `headerRight` | `ReactNode` | — | Header right slot |
796
+ | `colors` | `Partial<DrawerColors>` | — | Token overrides |
797
+
798
+ **DrawerNavItem:** `key`, `label`, `icon?`, `badge?`, `section?`, `active?`, `disabled?`, `onPress?`
799
+
800
+ ---
801
+
802
+ ### Collapse / CollapseGroup
803
+
804
+ Animated accordion panels with full render-slot control.
805
+
806
+ ```tsx
807
+ import { Collapse, CollapseGroup, CollapseItem, palettes, theme } from 'fluent-styles'
808
+
809
+ // --- Variants ---
810
+ <Collapse title="Cell (default)" variant="cell">…</Collapse>
811
+ <Collapse title="Card" subtitle="Shadow + radius" variant="card">…</Collapse>
812
+ <Collapse title="Bordered" variant="bordered">…</Collapse>
813
+ <Collapse title="Ghost" variant="ghost">…</Collapse>
814
+
815
+ // --- Sizes ---
816
+ <Collapse title="Small" variant="bordered" size="sm">…</Collapse>
817
+ <Collapse title="Medium" variant="bordered" size="md">…</Collapse>
818
+ <Collapse title="Large" variant="bordered" size="lg">…</Collapse>
819
+
820
+ // --- Header slots: leading · subtitle · trailing ---
821
+ <Collapse
822
+ variant="card"
823
+ leading={<Text style={{ fontSize: 20 }}>📦</Text>}
824
+ title="Leading icon"
825
+ subtitle="Any ReactNode on the left"
826
+ >…</Collapse>
827
+
828
+ <Collapse
829
+ variant="card"
830
+ title="Trailing badge"
831
+ trailing={<View style={{ backgroundColor: palettes.indigo[500], borderRadius: 10, paddingHorizontal: 8 }}><Text style={{ color: '#fff', fontWeight: '700' }}>NEW</Text></View>}
832
+ >…</Collapse>
833
+
834
+ // --- Active header tint ---
835
+ <Collapse title="Tints when open" variant="bordered" activeHeader>…</Collapse>
836
+
837
+ // --- Disabled ---
838
+ <Collapse title="Premium feature" subtitle="Upgrade to unlock" variant="bordered" disabled>…</Collapse>
839
+
840
+ // --- Default open (uncontrolled) ---
841
+ <Collapse title="Starts expanded" variant="card" defaultCollapse>…</Collapse>
842
+
843
+ // --- Controlled open state ---
844
+ const [open, setOpen] = useState(false)
845
+ <Collapse
846
+ title="Externally driven"
847
+ variant="bordered"
848
+ collapse={open}
849
+ onCollapse={setOpen}
850
+ >…</Collapse>
851
+
852
+ // --- Custom header renderer ---
853
+ <Collapse
854
+ variant="card"
855
+ renderHeader={(open) => (
856
+ <View style={{ padding: 14, backgroundColor: open ? '#eef2ff' : '#f2f2f7' }}>
857
+ <Text style={{ fontWeight: '600' }}>{open ? '▲ Open' : '▼ Closed'}</Text>
858
+ </View>
859
+ )}
860
+ >…</Collapse>
861
+
862
+ // --- Custom header right (keep title, replace right side) ---
863
+ <Collapse
864
+ title="Custom right"
865
+ variant="bordered"
866
+ renderHeaderRight={(open, chevron) => (
867
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
868
+ <Text style={{ color: open ? '#6366f1' : '#8e8e93' }}>{open ? 'Open' : 'Closed'}</Text>
869
+ {chevron}
870
+ </View>
871
+ )}
872
+ >…</Collapse>
873
+
874
+ // --- Color token overrides ---
875
+ <Collapse
876
+ title="Dark slate theme"
877
+ variant="card"
878
+ colors={{
879
+ background: theme.colors.blueGray[900],
880
+ border: theme.colors.blueGray[700],
881
+ titleColor: theme.colors.blueGray[100],
882
+ subtitleColor: theme.colors.blueGray[400],
883
+ iconColor: theme.colors.blueGray[400],
884
+ activeHeaderBg: palettes.blueGray[800],
885
+ }}
886
+ >…</Collapse>
887
+
888
+ <Collapse
889
+ title="Warm amber theme"
890
+ variant="bordered"
891
+ colors={{
892
+ background: palettes.amber[50],
893
+ border: palettes.amber[300],
894
+ titleColor: palettes.amber[900],
895
+ iconColor: palettes.amber[500],
896
+ activeHeaderBg: palettes.amber[100],
897
+ }}
898
+ >…</Collapse>
899
+
900
+ // --- CollapseGroup: multi-open with defaultActiveKey array ---
901
+ <CollapseGroup variant="bordered" defaultActiveKey={['shipping']}>
902
+ <CollapseItem itemKey="shipping" title="Shipping" subtitle="2–5 business days">…</CollapseItem>
903
+ <CollapseItem itemKey="returns" title="Returns" subtitle="30-day policy">…</CollapseItem>
904
+ <CollapseItem itemKey="sizing" title="Size guide">…</CollapseItem>
905
+ </CollapseGroup>
906
+
907
+ // --- CollapseGroup: accordion FAQ with icons ---
908
+ <CollapseGroup accordion variant="card" defaultActiveKey="q1" style={{ gap: 8 }}>
909
+ <CollapseItem itemKey="q1" leading={<Text style={{ fontSize: 18 }}>💳</Text>} title="Accepted payment methods">…</CollapseItem>
910
+ <CollapseItem itemKey="q2" leading={<Text style={{ fontSize: 18 }}>🔒</Text>} title="Is my data secure?">…</CollapseItem>
911
+ <CollapseItem itemKey="q3" leading={<Text style={{ fontSize: 18 }}>📞</Text>} title="How do I contact support?">…</CollapseItem>
912
+ </CollapseGroup>
913
+ ```
914
+
915
+ | Prop | Type | Default | Description |
916
+ |---|---|---|---|
917
+ | `title / subtitle` | `ReactNode` | — | Header content |
918
+ | `leading / trailing` | `ReactNode` | — | Header side slots |
919
+ | `variant` | `cell \| card \| bordered \| ghost` | `cell` | Visual style |
920
+ | `size` | `sm \| md \| lg` | `md` | Padding scale |
921
+ | `collapse / defaultCollapse` | `boolean` | — | Controlled / uncontrolled open state |
922
+ | `onCollapse` | `(open: boolean) => void` | — | Toggle callback |
923
+ | `activeHeader` | `boolean` | `false` | Tint header when open |
924
+ | `disabled` | `boolean` | `false` | Prevent interaction |
925
+ | `lazyRender` | `boolean` | `true` | Mount body on first expand |
926
+ | `destroyOnClose` | `boolean` | `false` | Unmount body when collapsed |
927
+ | `renderHeader` | `(open: boolean) => ReactNode` | — | Replace the entire header |
928
+ | `renderHeaderRight` | `(open, chevron) => ReactNode` | — | Replace only the right side of the header |
929
+ | `colors` | `Partial<CollapseColors>` | — | Token overrides |
930
+
931
+ `CollapseGroup` additional props: `accordion` (single-open), `defaultActiveKey` (`string \| string[]`)
932
+
933
+ ---
934
+
935
+ ### TabBar
936
+
937
+ A feature-rich animated tab bar with badge, icon, and indicator support.
938
+
939
+ ```tsx
940
+ import { TabBar, TabItem, palettes } from 'fluent-styles'
941
+
942
+ // --- Bottom nav with icons + dot badges ---
943
+ type Nav = 'home' | 'explore' | 'activity' | 'profile'
944
+ const NAV_TABS: TabItem<Nav>[] = [
945
+ { value: 'home', label: 'Home', iconRender: (c) => <HomeIcon color={c} /> },
946
+ { value: 'explore', label: 'Explore', iconRender: (c) => <SearchIcon color={c} />, badge: 3 },
947
+ { value: 'activity', label: 'Activity', iconRender: (c) => <BellIcon color={c} />, badge: '' }, // '' = dot badge
948
+ { value: 'profile', label: 'Profile', iconRender: (c) => <UserIcon color={c} /> },
949
+ ]
950
+ <TabBar options={NAV_TABS} value={nav} onChange={setNav} indicator="dot" showBorder />
951
+
952
+ // --- Animated underline indicator ---
953
+ <TabBar options={SIMPLE_TABS} value={seg} onChange={setSeg} indicator="line" showBorder />
954
+
955
+ // --- Sliding pill indicator ---
956
+ <TabBar
957
+ options={SIMPLE_TABS}
958
+ defaultValue="week"
959
+ indicator="pill"
960
+ colors={{ background: palettes.indigo[50], activeText: palettes.indigo[700], indicator: palettes.indigo[200], text: palettes.indigo[400] }}
961
+ />
962
+
963
+ // --- Scrollable tabs (many items) ---
964
+ <TabBar options={MANY_TABS} value={cat} onChange={setCat} tabAlign="scroll" indicator="line" showBorder />
965
+
966
+ // --- Disabled tabs ---
967
+ const TABS_WITH_DISABLED: TabItem<string>[] = [
968
+ { value: 'available', label: 'Available' },
969
+ { value: 'locked', label: 'Locked', disabled: true },
970
+ { value: 'open', label: 'Open' },
971
+ ]
972
+ <TabBar options={TABS_WITH_DISABLED} value={active} onChange={setActive} indicator="line" showBorder />
973
+
974
+ // --- Solid / chip variant ---
975
+ <TabBar
976
+ options={SIMPLE_TABS}
977
+ defaultValue="week"
978
+ variant="solid"
979
+ indicator="pill"
980
+ colors={{
981
+ background: palettes.gray[100],
982
+ activeChipBg: '#ffffff',
983
+ activeChipText: palettes.gray[900],
984
+ indicator: palettes.coolGray[200],
985
+ text: palettes.gray[500],
986
+ }}
987
+ />
988
+
989
+ // --- Numeric values (step wizard) ---
990
+ type Step = 1 | 2 | 3
991
+ const STEPS: TabItem<Step>[] = [
992
+ { value: 1, label: 'Step 1' },
993
+ { value: 2, label: 'Step 2' },
994
+ { value: 3, label: 'Step 3' },
995
+ ]
996
+ <TabBar
997
+ options={STEPS}
998
+ value={step}
999
+ onChange={setStep}
1000
+ indicator="line"
1001
+ colors={{ activeText: palettes.violet[600], indicator: palettes.violet[600], text: palettes.gray[400] }}
1002
+ showBorder
1003
+ />
1004
+
1005
+ // --- Custom indicator sizing ---
1006
+ <TabBar options={SIMPLE_TABS} defaultValue="month" indicator="line" indicatorWidth={24} indicatorHeight={3} indicatorRadius={3} showBorder />
1007
+
1008
+ // --- Label scale on active tab ---
1009
+ <TabBar options={SIMPLE_TABS} defaultValue="week" indicator="line" labelBulge={1.15} showBorder />
1010
+
1011
+ // --- Color overrides: green theme ---
1012
+ <TabBar
1013
+ options={SIMPLE_TABS}
1014
+ defaultValue="day"
1015
+ indicator="line"
1016
+ showBorder
1017
+ colors={{ background: palettes.green[50], activeText: palettes.green[700], indicator: palettes.green[500], text: palettes.green[400], border: palettes.green[200] }}
1018
+ />
1019
+
1020
+ // --- Color overrides: dark slate ---
1021
+ <TabBar
1022
+ options={NAV_TABS}
1023
+ defaultValue="home"
1024
+ indicator="dot"
1025
+ showBorder
1026
+ colors={{ background: palettes.blueGray[900], activeText: palettes.indigo[400], indicator: palettes.indigo[400], text: palettes.blueGray[400], border: palettes.blueGray[700], badge: palettes.rose[400] }}
1027
+ />
1028
+
1029
+ // --- Large font / taller bar ---
1030
+ <TabBar options={SIMPLE_TABS} defaultValue="week" indicator="line" fontSize={17} height={52} showBorder />
1031
+ ```
1032
+
1033
+ | Prop | Type | Default | Description |
1034
+ |---|---|---|---|
1035
+ | `options` | `TabItem<T>[]` | — | Tab definitions |
1036
+ | `value / defaultValue` | `T` | — | Controlled / uncontrolled value |
1037
+ | `onChange` | `(val: T) => void` | — | Change callback |
1038
+ | `variant` | `default \| underline \| card \| solid` | `default` | Visual preset |
1039
+ | `indicator` | `false \| line \| pill \| dot` | `false` | Animated indicator style |
1040
+ | `indicatorColor` | `ColorValue` | — | Indicator colour override |
1041
+ | `indicatorWidth` | `number` | auto | Fixed indicator width (0 = full tab width) |
1042
+ | `indicatorHeight` | `number` | `2` | Indicator thickness |
1043
+ | `indicatorRadius` | `number` | auto | Indicator border radius |
1044
+ | `tabAlign` | `center \| scroll` | `center` | Equal-width or scrolling tabs |
1045
+ | `labelBulge` | `number \| boolean` | `1` | Active label scale factor |
1046
+ | `fontSize` | `number` | — | Label font size |
1047
+ | `height` | `number` | — | Bar height override |
1048
+ | `showBorder` | `boolean` | `false` | Persistent bottom border |
1049
+ | `colors` | `Partial<TabBarColors>` | — | Token overrides |
1050
+
1051
+ **TabItem:** `value`, `label`, `badge?` (`number` = count, `''` = dot), `iconRender?`, `disabled?`
1052
+
1053
+ ---
1054
+
1055
+ ### StyledDivider
1056
+
1057
+ A simple horizontal rule.
1058
+
1059
+ ```tsx
1060
+ import { StyledDivider } from 'fluent-styles'
1061
+
1062
+ <StyledDivider />
1063
+ <StyledDivider borderBottomColor="#e4e4e7" marginVertical={8} />
1064
+ ```
1065
+
1066
+ ---
1067
+
1068
+ ### StyledSeperator
1069
+
1070
+ A horizontal row with left and optional right label — ideal for section headers.
1071
+
1072
+ ```tsx
1073
+ import { StyledSeperator } from 'fluent-styles'
1074
+
1075
+ <StyledSeperator leftLabel="Recent" rightLabel="See all" marginVertical={8} />
1076
+ ```
1077
+
1078
+ Props: `leftLabel`, `leftLabelProps`, `rightLabel`, `rightLabelProps`, plus all `StackProps`.
1079
+
1080
+ ---
1081
+
1082
+ ### Stack
1083
+
1084
+ A layout primitive for row and column arrangements.
1085
+
1086
+ ```tsx
1087
+ import { Stack } from 'fluent-styles'
1088
+
1089
+ // Vertical (column)
1090
+ <Stack gap={12}>
1091
+ <StyledText>Item 1</StyledText>
1092
+ <StyledText>Item 2</StyledText>
1093
+ </Stack>
1094
+
1095
+ // Horizontal (row)
1096
+ <Stack horizontal alignItems="center" gap={8}>
1097
+ <Avatar />
1098
+ <StyledText>Jane Doe</StyledText>
1099
+ </Stack>
1100
+ ```
1101
+
1102
+ ---
1103
+
1104
+ ### StyledText
1105
+
1106
+ A variant-aware Text component accepting `TextStyle` props directly.
1107
+
1108
+ ```tsx
1109
+ import { StyledText } from 'fluent-styles'
1110
+
1111
+ <StyledText fontSize={18} fontWeight="700" color="#1c1c1e">Heading</StyledText>
1112
+ <StyledText link>Click here</StyledText>
1113
+ <StyledText textAlign="center" color="#6b7280">Muted caption</StyledText>
1114
+ ```
1115
+
1116
+ ---
1117
+
1118
+ ### StyledPressable
1119
+
1120
+ A styled `Pressable` accepting flat `ViewStyle` props.
1121
+
1122
+ ```tsx
1123
+ import { StyledPressable } from 'fluent-styles'
1124
+
1125
+ <StyledPressable padding={12} borderRadius={8} backgroundColor="#f4f4f5" onPress={handlePress}>
1126
+ <StyledText>Press me</StyledText>
1127
+ </StyledPressable>
1128
+ ```
1129
+
1130
+ ---
1131
+
1132
+ ### StyledPage / StyledScrollView
1133
+
1134
+ Base layout containers.
1135
+
1136
+ ```tsx
1137
+ import { StyledPage, StyledScrollView } from 'fluent-styles'
1138
+
1139
+ <StyledPage flex={1} backgroundColor="#f8f8f8">
1140
+ <StyledScrollView contentContainerStyle={{ padding: 16 }}>
1141
+ <MyContent />
1142
+ </StyledScrollView>
1143
+ </StyledPage>
1144
+ ```
1145
+
1146
+ ---
1147
+
1148
+ ### StyledSafeAreaView
1149
+
1150
+ A styled wrapper around `SafeAreaView`.
1151
+
1152
+ ```tsx
1153
+ import { StyledSafeAreaView } from 'fluent-styles'
1154
+
1155
+ <StyledSafeAreaView flex={1} backgroundColor="#fff">
1156
+ <App />
1157
+ </StyledSafeAreaView>
1158
+ ```
1159
+
1160
+ ---
1161
+
1162
+ ### Spacer
1163
+
1164
+ Inserts fixed or flexible whitespace.
1165
+
1166
+ ```tsx
1167
+ import { Spacer } from 'fluent-styles'
1168
+
1169
+ <Spacer height={16} />
1170
+ <Spacer flex={1} /> {/* fills remaining space */}
1171
+ ```
1172
+
1173
+ ---
1174
+
1175
+ ### StyledShape
1176
+
1177
+ A shaped container for icon chips, avatars, and dot indicators.
1178
+
1179
+ ```tsx
1180
+ import { StyledShape } from 'fluent-styles'
1181
+
1182
+ <StyledShape size={40} borderRadius={20} backgroundColor="#6366f1">
1183
+ <StyledText color="#fff">A</StyledText>
1184
+ </StyledShape>
1185
+ ```
1186
+
1187
+ ---
1188
+
1189
+ ### Loader
1190
+
1191
+ A loading indicator with four animation variants, optional overlay, and label.
1192
+
1193
+ ```tsx
1194
+ import { Loader } from 'fluent-styles'
1195
+
1196
+ <Loader variant="spinner" />
1197
+ <Loader variant="dots" color="#6366f1" label="Saving…" />
1198
+ <Loader variant="pulse" overlay />
1199
+ <Loader variant="circular" label="Loading…" theme="dark" />
1200
+ ```
1201
+
1202
+ | Prop | Type | Default | Description |
1203
+ |---|---|---|---|
1204
+ | `variant` | `spinner \| pulse \| dots \| circular` | `spinner` | Animation style |
1205
+ | `label` | `string` | — | Text below the indicator |
1206
+ | `color` | `string` | — | Indicator tint colour |
1207
+ | `overlay` | `boolean` | `false` | Full-screen dimmed backdrop |
1208
+ | `theme` | `light \| dark \| system` | `system` | Colour scheme |
1209
+ | `colors` | `Partial<LoaderColors>` | — | Fine-grained token overrides |
1210
+
1211
+ ---
1212
+
1213
+ ### StyledCircularProgress
1214
+
1215
+ An animated SVG ring progress indicator with four visual variants, five preset sizes, centre label modes, gradient support, and full colour customisation.
1216
+
1217
+ ```tsx
1218
+ import { StyledCircularProgress } from 'fluent-styles'
1219
+
1220
+ // Basic — shows percentage
1221
+ <StyledCircularProgress value={72} />
1222
+
1223
+ // Fraction display
1224
+ <StyledCircularProgress value={18} total={25} display="fraction" />
1225
+
1226
+ // With label and sublabel
1227
+ <StyledCircularProgress
1228
+ value={72}
1229
+ display="percent"
1230
+ label="Tasks"
1231
+ sublabel="this week"
1232
+ size="lg"
1233
+ />
1234
+
1235
+ // Gradient variant
1236
+ <StyledCircularProgress
1237
+ value={68}
1238
+ variant="gradient"
1239
+ colors={{
1240
+ gradientFrom: '#6366f1',
1241
+ gradientTo: '#22d3ee',
1242
+ }}
1243
+ />
1244
+
1245
+ // Dashboard (half-circle gauge)
1246
+ <StyledCircularProgress value={55} variant="dashboard" size="xl" />
1247
+
1248
+ // Custom diameter and stroke
1249
+ <StyledCircularProgress value={55} diameter={120} strokeWidth={24} display="percent" />
1250
+
1251
+ // Colour overrides
1252
+ <StyledCircularProgress
1253
+ value={82}
1254
+ display="percent"
1255
+ label="Health"
1256
+ size="md"
1257
+ colors={{
1258
+ arc: theme.colors.green[500],
1259
+ track: theme.colors.green[100],
1260
+ label: theme.colors.green[700],
1261
+ sublabel: theme.colors.green[400],
1262
+ }}
1263
+ />
1264
+
1265
+ // Custom centre content (overrides display/label)
1266
+ <StyledCircularProgress value={55} display="none" size="lg">
1267
+ <Stack alignItems="center" gap={2}>
1268
+ <StyledText fontSize={14}>❤️</StyledText>
1269
+ <StyledText fontSize={12} fontWeight={theme.fontWeight.bold} color="#f43f5e">
1270
+ 55 bpm
1271
+ </StyledText>
1272
+ </Stack>
1273
+ </StyledCircularProgress>
1274
+
1275
+ // Controlled value with animation
1276
+ <StyledCircularProgress
1277
+ value={controlled}
1278
+ display="percent"
1279
+ label="Progress"
1280
+ sublabel={`${controlled} / 100`}
1281
+ size="xl"
1282
+ variant="gradient"
1283
+ duration={600}
1284
+ />
1285
+
1286
+ // No animation
1287
+ <StyledCircularProgress value={90} animated={false} display="percent" />
1288
+
1289
+ // On a dark surface
1290
+ <StyledCircularProgress
1291
+ value={72}
1292
+ variant="gradient"
1293
+ display="percent"
1294
+ label="Progress"
1295
+ colors={{
1296
+ gradientFrom: '#818cf8',
1297
+ gradientTo: '#22d3ee',
1298
+ track: 'rgba(255,255,255,0.12)',
1299
+ label: '#f4f4f5',
1300
+ sublabel: 'rgba(255,255,255,0.5)',
1301
+ }}
1302
+ />
1303
+ ```
1304
+
1305
+ #### Props
1306
+
1307
+ | Prop | Type | Default | Description |
1308
+ |---|---|---|---|
1309
+ | `value` | `number` | **required** | Current progress value |
1310
+ | `total` | `number` | `100` | Maximum value |
1311
+ | `display` | `'percent' \| 'fraction' \| 'value' \| 'label' \| 'none'` | `'percent'` | What to render in the centre |
1312
+ | `label` | `string` | — | Primary label inside the ring |
1313
+ | `sublabel` | `string` | — | Secondary line below the primary label |
1314
+ | `variant` | `'default' \| 'ghost' \| 'gradient' \| 'dashboard'` | `'default'` | Visual style |
1315
+ | `size` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | Preset diameter |
1316
+ | `diameter` | `number` | — | Pixel diameter — overrides `size` |
1317
+ | `strokeWidth` | `number` | — | Arc thickness in px — auto-scaled when omitted |
1318
+ | `lineCap` | `'round' \| 'butt' \| 'square'` | `'round'` | Arc end cap style |
1319
+ | `animated` | `boolean` | `true` | Animate from 0 to `value` on mount / value change |
1320
+ | `duration` | `number` | `900` | Animation duration in ms |
1321
+ | `colors` | `Partial<CircularProgressColors>` | — | Fine-grained colour overrides (see below) |
1322
+ | `children` | `ReactNode` | — | Custom centre content — overrides `display`, `label`, `sublabel` |
1323
+
1324
+ #### Size presets
1325
+
1326
+ | Size | Diameter | Stroke |
1327
+ |---|---|---|
1328
+ | `xs` | 48 px | 4 px |
1329
+ | `sm` | 64 px | 5 px |
1330
+ | `md` | 80 px | 6 px |
1331
+ | `lg` | 100 px | 7 px |
1332
+ | `xl` | 128 px | 8 px |
1333
+
1334
+ #### Variants
1335
+
1336
+ | Variant | Description |
1337
+ |---|---|
1338
+ | `default` | Coloured arc on a light grey track |
1339
+ | `ghost` | Arc only — no background track |
1340
+ | `gradient` | Two-stop linear gradient arc (uses `gradientFrom` / `gradientTo`) |
1341
+ | `dashboard` | Half-circle gauge — flat side at the bottom |
1342
+
1343
+ #### `CircularProgressColors`
1344
+
1345
+ | Token | Default | Controls |
1346
+ |---|---|---|
1347
+ | `arc` | `indigo[500]` | Progress arc fill |
1348
+ | `track` | `gray[200]` | Background track ring |
1349
+ | `label` | `gray[800]` | Primary centre text |
1350
+ | `sublabel` | `gray[400]` | Secondary centre text |
1351
+ | `gradientFrom` | `violet[500]` | Gradient start — `gradient` variant only |
1352
+ | `gradientTo` | `cyan[400]` | Gradient end — `gradient` variant only |
1353
+
1354
+ #### Real-world example — onboarding card
1355
+
1356
+ ```tsx
1357
+ <Stack
1358
+ backgroundColor={theme.colors.indigo[600]}
1359
+ borderRadius={16}
1360
+ horizontal
1361
+ gap={20}
1362
+ paddingVertical={32}
1363
+ paddingHorizontal={16}
1364
+ alignItems="center"
1365
+ >
1366
+ <StyledCircularProgress
1367
+ value={3}
1368
+ total={5}
1369
+ display="fraction"
1370
+ label="done"
1371
+ size="lg"
1372
+ colors={{
1373
+ arc: theme.colors.white,
1374
+ track: 'rgba(255,255,255,0.2)',
1375
+ label: theme.colors.white,
1376
+ sublabel: 'rgba(255,255,255,0.65)',
1377
+ }}
1378
+ />
1379
+ <Stack vertical flex={1} gap={4}>
1380
+ <StyledText fontSize={16} fontWeight={theme.fontWeight.bold} color={theme.colors.white}>
1381
+ Getting started
1382
+ </StyledText>
1383
+ <StyledText fontSize={13} color="rgba(255,255,255,0.75)">
1384
+ Complete 2 more steps to unlock all features.
1385
+ </StyledText>
1386
+ </Stack>
1387
+ </Stack>
1388
+ ```
1389
+
1390
+ ---
1391
+
1392
+ ### StyledChip
1393
+
1394
+ A multi-variant chip/tag component with controlled and uncontrolled selection, animated checkmarks, and six visual variants.
1395
+
1396
+ ```tsx
1397
+ import { StyledChip } from 'fluent-styles'
1398
+ ```
1399
+
1400
+ #### Variants
1401
+
1402
+ | Variant | Description |
1403
+ |---|---|
1404
+ | `outlined` | Border + tinted bg when selected (default) |
1405
+ | `filled` | Solid background, changes tone on select |
1406
+ | `smooth` | Soft grey background, no border |
1407
+ | `ingredient` | Dark-bg when selected (recipe/filter chips) |
1408
+ | `likeable` | Pink heart chip — toggles like state |
1409
+ | `icon` | Leading icon with accent styling |
1410
+
1411
+ #### Size presets
1412
+
1413
+ | Size | Padding H | Padding V | Font | Icon | Radius |
1414
+ |---|---|---|---|---|---|
1415
+ | `sm` | 10 | 5 | 11 | 12 | 20 |
1416
+ | `md` | 14 | 8 | 13 | 14 | 24 |
1417
+ | `lg` | 18 | 11 | 15 | 16 | 28 |
1418
+
1419
+ #### Props
1420
+
1421
+ | Prop | Type | Default | Description |
1422
+ |---|---|---|---|
1423
+ | `label` | `string` | — | Chip text |
1424
+ | `variant` | `ChipVariant` | `'outlined'` | Visual style |
1425
+ | `size` | `ChipSize` | `'md'` | Size preset |
1426
+ | `selected` | `boolean` | — | Controlled selection state |
1427
+ | `defaultSelected` | `boolean` | `false` | Uncontrolled initial state |
1428
+ | `onPress` | `(selected: boolean) => void` | — | Fires with new selection value |
1429
+ | `color` | `string` | theme default | Accent colour (border, text, icon) |
1430
+ | `bgColor` | `string` | variant default | Fill background |
1431
+ | `icon` | `React.ReactNode` | — | Leading icon node (used with `'icon'` variant) |
1432
+ | `showCheck` | `boolean` | `true` | Show checkmark when selected |
1433
+ | `disabled` | `boolean` | `false` | Reduces opacity, disables press |
1434
+ | `borderRadius` | `number` | size preset | Override border radius |
1435
+
1436
+ #### Usage
1437
+
1438
+ ```tsx
1439
+ import React, { useState } from 'react'
1440
+ import { StyledChip, Stack } from 'fluent-styles'
1441
+ import Icon from 'react-native-vector-icons/Feather'
1442
+
1443
+ // ── Multi-select toggle helper ────────────────────────────────────────────────
1444
+ // A common pattern: maintain a string[] of selected labels and toggle them.
1445
+ const [selected, setSelected] = useState<string[]>(['Hacktoberfest'])
1446
+
1447
+ const toggle = (label: string) =>
1448
+ setSelected((prev) =>
1449
+ prev.includes(label) ? prev.filter((l) => l !== label) : [...prev, label]
1450
+ )
1451
+
1452
+ // ── 1. Outlined — multi-select filter bar ─────────────────────────────────────
1453
+ <Stack gap={10}>
1454
+ <Stack horizontal gap={8} flexWrap="wrap">
1455
+ {[
1456
+ { label: 'Enhancement', color: '#9e9e9e' },
1457
+ { label: 'Trends', color: '#ff9800' },
1458
+ ].map(({ label, color }) => (
1459
+ <StyledChip
1460
+ key={label}
1461
+ label={label}
1462
+ variant="outlined"
1463
+ color={color}
1464
+ selected={selected.includes(label)}
1465
+ onPress={() => toggle(label)}
1466
+ />
1467
+ ))}
1468
+ </Stack>
1469
+ <Stack horizontal gap={8} flexWrap="wrap">
1470
+ {[
1471
+ { label: 'Rubi Kapustu', color: '#2196f3' },
1472
+ { label: 'Hacktoberfest', color: '#4caf50' },
1473
+ ].map(({ label, color }) => (
1474
+ <StyledChip
1475
+ key={label}
1476
+ label={label}
1477
+ variant="outlined"
1478
+ color={color}
1479
+ selected={selected.includes(label)}
1480
+ onPress={() => toggle(label)}
1481
+ />
1482
+ ))}
1483
+ </Stack>
1484
+ <Stack horizontal gap={8} flexWrap="wrap">
1485
+ {[
1486
+ { label: 'Limited', color: '#e65100' },
1487
+ { label: 'Taken', color: '#e91e63' },
1488
+ ].map(({ label, color }) => (
1489
+ <StyledChip key={label} label={label} variant="outlined" color={color}
1490
+ selected={selected.includes(label)} onPress={() => toggle(label)} />
1491
+ ))}
1492
+ </Stack>
1493
+ </Stack>
1494
+
1495
+ // ── 2. Ingredient — recipe / dietary filter ───────────────────────────────────
1496
+ const [ingredients, setIngredients] = useState(['Cinnamon', 'Nut'])
1497
+
1498
+ {[['Cheese', 'Vanilla', 'Chocolate', 'Egg'],
1499
+ ['Honey', 'Milk', 'Banana', 'Nut'],
1500
+ ['Cinnamon', 'Tomato', 'Yogurt'],
1501
+ ].map((row, ri) => (
1502
+ <Stack key={ri} horizontal gap={8} flexWrap="wrap">
1503
+ {row.map((label) => (
1504
+ <StyledChip
1505
+ key={label}
1506
+ label={label}
1507
+ variant="ingredient"
1508
+ selected={ingredients.includes(label)}
1509
+ onPress={() => toggle(label)}
1510
+ />
1511
+ ))}
1512
+ </Stack>
1513
+ ))}
1514
+
1515
+ // ── 3. Filled — label/status chips ────────────────────────────────────────────
1516
+ <Stack horizontal gap={8} flexWrap="wrap">
1517
+ <StyledChip label="Hacktoberfest" variant="filled" bgColor="#e8f5e9" color="#388e3c" />
1518
+ <StyledChip label="Question" variant="filled" bgColor="#fff3e0" color="#f57c00" />
1519
+ <StyledChip label="Enhancement" variant="filled" bgColor="#f3e5f5" color="#7b1fa2" />
1520
+ {/* Selected filled chip with checkmark */}
1521
+ <StyledChip
1522
+ label="Taken"
1523
+ variant="filled"
1524
+ bgColor="#e91e8c"
1525
+ color="#fff"
1526
+ defaultSelected
1527
+ showCheck
1528
+ />
1529
+ </Stack>
1530
+
1531
+ // ── 4. Icon chips — dynamic icon colour based on selected state ───────────────
1532
+ const [activeIcons, setActiveIcons] = useState<string[]>(['Social Media'])
1533
+
1534
+ <Stack horizontal gap={8} flexWrap="wrap">
1535
+ {/* Solid active: bgColor becomes fill when selected */}
1536
+ <StyledChip
1537
+ label="Social Media"
1538
+ variant="icon"
1539
+ icon={
1540
+ <Icon
1541
+ name="refresh-cw"
1542
+ size={14}
1543
+ color={activeIcons.includes('Social Media') ? '#fff' : '#2e7d32'}
1544
+ />
1545
+ }
1546
+ color="#2e7d32"
1547
+ bgColor="#2e7d32"
1548
+ selected={activeIcons.includes('Social Media')}
1549
+ onPress={() => toggle('Social Media')}
1550
+ />
1551
+ <StyledChip
1552
+ label="Pin"
1553
+ variant="icon"
1554
+ icon={<Icon name="map-pin" size={14} color="#2e7d32" />}
1555
+ color="#2e7d32"
1556
+ bgColor="#e8f5e9"
1557
+ selected={activeIcons.includes('Pin')}
1558
+ onPress={() => toggle('Pin')}
1559
+ />
1560
+ <StyledChip
1561
+ label="Activity"
1562
+ variant="icon"
1563
+ icon={<Icon name="activity" size={14} color="#2e7d32" />}
1564
+ color="#2e7d32"
1565
+ bgColor="#e8f5e9"
1566
+ selected={activeIcons.includes('Activity')}
1567
+ onPress={() => toggle('Activity')}
1568
+ />
1569
+ </Stack>
1570
+
1571
+ // Icon chips — mixed accent colour palette
1572
+ <Stack horizontal gap={8} flexWrap="wrap">
1573
+ <StyledChip label="Annotation" variant="icon"
1574
+ icon={<Icon name="edit-3" size={14} color="#5c6bc0" />} color="#5c6bc0" bgColor="#e8eaf6" />
1575
+ <StyledChip label="Laboratory" variant="icon"
1576
+ icon={<Icon name="zap" size={14} color="#5c6bc0" />} color="#5c6bc0" bgColor="#e8eaf6" />
1577
+ <StyledChip label="History" variant="icon"
1578
+ icon={<Icon name="clock" size={14} color="#5c6bc0" />} color="#5c6bc0" bgColor="#e8eaf6" />
1579
+ {/* Solid active — pre-selected */}
1580
+ <StyledChip
1581
+ label="Globe"
1582
+ variant="icon"
1583
+ icon={<Icon name="globe" size={14} color="#fff" />}
1584
+ color="#fff"
1585
+ bgColor="#3f51b5"
1586
+ selected
1587
+ onPress={() => {}}
1588
+ />
1589
+ </Stack>
1590
+
1591
+ // ── 5. Likeable — topic interest chips ───────────────────────────────────────
1592
+ const [liked, setLiked] = useState(['Big Data', 'New Technology'])
1593
+
1594
+ {[['Cryptocurrency', 'Big Data'],
1595
+ ['Software Development'],
1596
+ ['New Technology', 'Gadgets'],
1597
+ ['Technology Startups'],
1598
+ ].map((row, ri) => (
1599
+ <Stack key={ri} horizontal gap={8} flexWrap="wrap">
1600
+ {row.map((label) => (
1601
+ <StyledChip
1602
+ key={label}
1603
+ label={label}
1604
+ variant="likeable"
1605
+ selected={liked.includes(label)}
1606
+ onPress={() => toggle(label)}
1607
+ />
1608
+ ))}
1609
+ </Stack>
1610
+ ))}
1611
+
1612
+ // ── 6. Size variants side-by-side ────────────────────────────────────────────
1613
+ <Stack gap={10}>
1614
+ <Stack horizontal gap={8} alignItems="center">
1615
+ <StyledChip label="Small" variant="outlined" size="sm" color="#2196f3" />
1616
+ <StyledChip label="Medium" variant="outlined" size="md" color="#2196f3" />
1617
+ <StyledChip label="Large" variant="outlined" size="lg" color="#2196f3" />
1618
+ </Stack>
1619
+ <Stack horizontal gap={8} alignItems="center">
1620
+ <StyledChip label="Small" variant="ingredient" size="sm" defaultSelected />
1621
+ <StyledChip label="Medium" variant="ingredient" size="md" defaultSelected />
1622
+ <StyledChip label="Large" variant="ingredient" size="lg" defaultSelected />
1623
+ </Stack>
1624
+ </Stack>
1625
+
1626
+ // ── 7. Disabled state ────────────────────────────────────────────────────────
1627
+ <Stack horizontal gap={8} flexWrap="wrap">
1628
+ <StyledChip label="Outlined" variant="outlined" color="#2196f3" disabled />
1629
+ <StyledChip label="Filled" variant="filled" bgColor="#e91e63" color="#fff" disabled />
1630
+ <StyledChip label="Ingredient" variant="ingredient" disabled />
1631
+ <StyledChip label="Likeable" variant="likeable" disabled />
1632
+ </Stack>
1633
+ ```
1634
+
1635
+ ---
1636
+
1637
+ ### StyledBar
1638
+
1639
+ An animated SVG bar chart with gradient active bars, optional hatch texture on inactive bars, and a floating tooltip. Backed by `react-native-svg`.
1640
+
1641
+ ```tsx
1642
+ import { StyledBar } from 'fluent-styles'
1643
+ ```
1644
+
1645
+ #### `StyledBarDatum`
1646
+
1647
+ | Field | Type | Description |
1648
+ |---|---|---|
1649
+ | `label` | `string` | X-axis label |
1650
+ | `value` | `number \| null` | Bar height value; `null` renders a grey placeholder bar |
1651
+ | `active` | `boolean` | Marks the active/highlighted bar (renders gradient + tooltip) |
1652
+
1653
+ #### `StyledBarColors`
1654
+
1655
+ | Field | Default | Description |
1656
+ |---|---|---|
1657
+ | `inactiveBar` | `gray[200]` | Inactive bar fill |
1658
+ | `hatchLine` | `rgba(0,0,0,0.07)` | Hatch stripe colour on inactive bars |
1659
+ | `activeTop` | `#d4f53c` | Active bar gradient top |
1660
+ | `activeBottom` | `#a8c820` | Active bar gradient bottom |
1661
+ | `tooltipBg` | `gray[900]` | Tooltip bubble background |
1662
+ | `tooltipText` | `white` | Tooltip text colour |
1663
+ | `activeLabelColor` | `gray[900]` | Label colour for active bar |
1664
+ | `inactiveLabelColor` | `gray[400]` | Label colour for inactive bars |
1665
+
1666
+ #### Props
1667
+
1668
+ | Prop | Type | Default | Description |
1669
+ |---|---|---|---|
1670
+ | `data` | `StyledBarDatum[]` | — | Array of bar data |
1671
+ | `maxValue` | `number` | max of values | Explicit Y-axis ceiling |
1672
+ | `width` | `number` | screen width − padding | SVG canvas width in px |
1673
+ | `containerPaddingHorizontal` | `number` | `80` | Horizontal padding to subtract from screen width |
1674
+ | `height` | `number` | `180` | Plot area height in px |
1675
+ | `barWidthRatio` | `number` | `0.62` | Bar width as fraction of slot width |
1676
+ | `labelHeight` | `number` | `28` | Height reserved below bars for labels |
1677
+ | `showHatch` | `boolean` | `true` | Render hatch texture on inactive bars |
1678
+ | `hatchSpacing` | `number` | `8` | Pixel gap between hatch lines |
1679
+ | `tooltipLabel` | `string` | auto from active value | Override tooltip text |
1680
+ | `unit` | `string` | `''` | Unit suffix appended to auto tooltip (e.g. `'min'`) |
1681
+ | `colors` | `StyledBarColors` | lime theme | Colour overrides |
1682
+ | `animated` | `boolean` | `true` | Animate bars growing from zero on mount |
1683
+ | `animationDuration` | `number` | `600` | Animation duration in ms |
1684
+
1685
+ #### Usage
1686
+
1687
+ ```tsx
1688
+ import { StyledBar, StyledCard, palettes, theme } from 'fluent-styles'
1689
+
1690
+ // ── Padding rule ──────────────────────────────────────────────────────────────
1691
+ // The chart auto-sizes to: screenWidth − containerPaddingHorizontal
1692
+ // When placed inside a card:
1693
+ // screen paddingHorizontal = 20 → both sides = 40
1694
+ // card padding = 20 → both sides = 40
1695
+ // total containerPaddingHorizontal = 80 ← pass this value
1696
+ const CONTAINER_PAD = 80
1697
+
1698
+ // ── 1. Default lime — workout duration ───────────────────────────────────────
1699
+ <StyledCard padding={20} shadow="light">
1700
+ <StyledBar
1701
+ data={[
1702
+ { label: 'Sat', value: 45 },
1703
+ { label: 'Sun', value: 60 },
1704
+ { label: 'Mon', value: 35 },
1705
+ { label: 'Tue', value: 70, active: true },
1706
+ { label: 'Wed', value: 50 },
1707
+ { label: 'Thu', value: 30 },
1708
+ { label: 'Fri', value: 20 },
1709
+ ]}
1710
+ unit="min"
1711
+ maxValue={100}
1712
+ containerPaddingHorizontal={CONTAINER_PAD}
1713
+ />
1714
+ </StyledCard>
1715
+
1716
+ // ── 2. Green theme — weight tracking with null placeholders ──────────────────
1717
+ // null value renders a shorter grey placeholder bar (e.g. a rest/missing day)
1718
+ <StyledBar
1719
+ data={[
1720
+ { label: '13', value: null }, // missing — shows placeholder
1721
+ { label: '14', value: 60.0, active: true },
1722
+ { label: '15', value: 58.2 },
1723
+ { label: '16', value: 59.1 },
1724
+ { label: '17', value: 57.4 },
1725
+ { label: '18', value: 58.0 },
1726
+ { label: '19', value: 56.8 },
1727
+ ]}
1728
+ unit="kg"
1729
+ maxValue={80}
1730
+ containerPaddingHorizontal={CONTAINER_PAD}
1731
+ colors={{
1732
+ activeTop: '#4ade80',
1733
+ activeBottom: '#16a34a',
1734
+ tooltipBg: '#15803d',
1735
+ tooltipText: '#fff',
1736
+ }}
1737
+ />
1738
+
1739
+ // ── 3. Orange — temperature, no hatch texture ─────────────────────────────────
1740
+ <StyledBar
1741
+ data={[
1742
+ { label: '13', value: null },
1743
+ { label: '14', value: 36.9, active: true },
1744
+ { label: '15', value: 37.1 },
1745
+ { label: '16', value: 36.8 },
1746
+ { label: '17', value: 37.0 },
1747
+ { label: '18', value: 37.2 },
1748
+ { label: '19', value: 36.5 },
1749
+ ]}
1750
+ unit="°C"
1751
+ maxValue={38}
1752
+ showHatch={false}
1753
+ containerPaddingHorizontal={CONTAINER_PAD}
1754
+ colors={{
1755
+ inactiveBar: '#fed7aa',
1756
+ activeTop: '#fb923c',
1757
+ activeBottom: '#ea580c',
1758
+ tooltipBg: '#c2410c',
1759
+ tooltipText: '#fff',
1760
+ }}
1761
+ />
1762
+
1763
+ // ── 4. Blue — water intake, large values ──────────────────────────────────────
1764
+ <StyledBar
1765
+ data={[
1766
+ { label: '13', value: null },
1767
+ { label: '14', value: 1750, active: true },
1768
+ { label: '15', value: 2100 },
1769
+ { label: '16', value: 1600 },
1770
+ { label: '17', value: 1900 },
1771
+ { label: '18', value: 800 },
1772
+ { label: '19', value: null },
1773
+ ]}
1774
+ unit="mL"
1775
+ maxValue={2500}
1776
+ containerPaddingHorizontal={CONTAINER_PAD}
1777
+ colors={{
1778
+ inactiveBar: '#bfdbfe',
1779
+ hatchLine: 'rgba(59,130,246,0.15)',
1780
+ activeTop: '#60a5fa',
1781
+ activeBottom: '#2563eb',
1782
+ tooltipBg: '#1e3a8a',
1783
+ tooltipText: '#fff',
1784
+ }}
1785
+ />
1786
+
1787
+ // ── 5. Rose — calories burned, overridden tooltip label ───────────────────────
1788
+ // tooltipLabel lets you display a formatted string instead of the raw value
1789
+ <StyledBar
1790
+ data={caloriesData}
1791
+ unit="kcal"
1792
+ maxValue={2500}
1793
+ tooltipLabel="2,200 kcal"
1794
+ containerPaddingHorizontal={CONTAINER_PAD}
1795
+ colors={{
1796
+ inactiveBar: '#fce7f3',
1797
+ hatchLine: 'rgba(236,72,153,0.12)',
1798
+ activeTop: '#f472b6',
1799
+ activeBottom: '#db2777',
1800
+ tooltipBg: '#831843',
1801
+ tooltipText: '#fff',
1802
+ activeLabelColor: '#be185d', // active x-label gets accent colour too
1803
+ }}
1804
+ />
1805
+
1806
+ // ── 6. Minimal — no animation, narrow bars, indigo ───────────────────────────
1807
+ // Use animated=false for static/print-style charts.
1808
+ // barWidthRatio controls how wide bars are relative to their slot.
1809
+ <StyledBar
1810
+ data={stepsData}
1811
+ unit="k"
1812
+ maxValue={100}
1813
+ animated={false}
1814
+ showHatch={false}
1815
+ barWidthRatio={0.42}
1816
+ containerPaddingHorizontal={CONTAINER_PAD}
1817
+ colors={{
1818
+ inactiveBar: '#e0e7ff',
1819
+ activeTop: '#818cf8',
1820
+ activeBottom: '#4338ca',
1821
+ tooltipBg: '#312e81',
1822
+ tooltipText: '#fff',
1823
+ }}
1824
+ />
1825
+ ```
1826
+
1827
+ ---
1828
+
1829
+ ### StyledTimeline
1830
+
1831
+ A data-driven vertical timeline with animated dots, three density variants, three dot shapes, custom renderers, and full colour overrides.
1832
+
1833
+ ```tsx
1834
+ import { StyledTimeline } from 'fluent-styles'
1835
+ ```
1836
+
1837
+ #### `TimelineItem`
1838
+
1839
+ | Field | Type | Description |
1840
+ |---|---|---|
1841
+ | `id` | `string` | Unique identifier |
1842
+ | `time` | `string` | Primary time label (e.g. `'09:00'`) |
1843
+ | `endTime` | `string` | Optional end time shown smaller below |
1844
+ | `title` | `string` | Bold title in default content renderer |
1845
+ | `subtitle` | `string` | Secondary line (muted) |
1846
+ | `description` | `string` | Tertiary detail line |
1847
+ | `content` | `React.ReactNode` | Custom content — replaces default renderer for this item |
1848
+ | `meta` | `Record<string, unknown>` | Arbitrary metadata for use in `renderItem` |
1849
+
1850
+ #### Variants
1851
+
1852
+ | Variant | Gap between rows |
1853
+ |---|---|
1854
+ | `compact` | 12 px |
1855
+ | `default` | 20 px |
1856
+ | `spacious` | 32 px |
1857
+
1858
+ #### Dot shapes
1859
+
1860
+ | Shape | Appearance |
1861
+ |---|---|
1862
+ | `circle` | Hollow ring (transparent fill, coloured border) |
1863
+ | `ring` | White fill with coloured border |
1864
+ | `filled` | Solid fill (default) |
1865
+
1866
+ #### `StyledTimelineColors`
1867
+
1868
+ | Field | Default | Description |
1869
+ |---|---|---|
1870
+ | `line` | `gray[200]` | Vertical connector line colour |
1871
+ | `dot` | `#8bc34a` | Dot fill / border colour |
1872
+ | `dotBorder` | `white` | Inner ring background (for `ring` shape) |
1873
+ | `timeText` | `gray[900]` | Primary time label colour |
1874
+ | `endTimeText` | `gray[400]` | End-time label colour |
1875
+
1876
+ #### Props
1877
+
1878
+ | Prop | Type | Default | Description |
1879
+ |---|---|---|---|
1880
+ | `items` | `TimelineItem[]` | `[]` | Data-driven item list |
1881
+ | `renderItem` | `(item, index) => ReactNode` | — | Custom row content renderer |
1882
+ | `children` | `ReactNode` | — | Extra rows appended after `items` |
1883
+ | `variant` | `TimelineVariant` | `'default'` | Row density |
1884
+ | `dotShape` | `TimelineDotShape` | `'filled'` | Dot style |
1885
+ | `dotSize` | `number` | `10` | Dot diameter in px |
1886
+ | `timeColumnWidth` | `number` | `56` | Width of left time column in px |
1887
+ | `timeGap` | `number` | `16` | Horizontal gap between dot column and content |
1888
+ | `animated` | `boolean` | `true` | Pop-in animation on each dot |
1889
+ | `colors` | `StyledTimelineColors` | — | Colour overrides |
1890
+ | `onItemPress` | `(item: TimelineItem) => void` | — | Press handler for rows |
1891
+
1892
+ #### Usage
1893
+
1894
+ ```tsx
1895
+ import { StyledTimeline, type TimelineItem } from 'fluent-styles'
1896
+
1897
+ // ── 1. Minimal JSON-driven ────────────────────────────────────────────────────
1898
+ <StyledTimeline
1899
+ items={[
1900
+ { id: '1', time: '09:00', title: 'Morning Run', subtitle: 'Cardio · 5km' },
1901
+ { id: '2', time: '11:30', title: 'Strength Class', subtitle: 'Upper body' },
1902
+ { id: '3', time: '14:00', title: 'Yoga', subtitle: 'Recovery' },
1903
+ ]}
1904
+ />
1905
+
1906
+ // ── 2. With end time ─────────────────────────────────────────────────────────
1907
+ // endTime is shown smaller below the main time label
1908
+ <StyledTimeline
1909
+ items={[
1910
+ { id: '1', time: '11:35', endTime: '13:05', title: 'Cardio', subtitle: '4 of 6 sessions' },
1911
+ { id: '2', time: '14:45', endTime: '15:45', title: 'Muscle', subtitle: '5 of 8 sessions' },
1912
+ { id: '3', time: '17:00', endTime: '18:00', title: 'Weight Training', subtitle: '4 of 9 sessions' },
1913
+ ]}
1914
+ colors={{ dot: '#8bc34a', timeText: '#1a1a1e', endTimeText: '#9ca3af' }}
1915
+ />
1916
+
1917
+ // ── 3. Custom renderItem — fitness planner card ───────────────────────────────
1918
+ // Store arbitrary per-item data in the meta field; cast it inside renderItem.
1919
+ interface WorkoutMeta {
1920
+ [key: string]: unknown
1921
+ iconName: string
1922
+ calories: string
1923
+ bpm: string
1924
+ duration: string
1925
+ }
1926
+
1927
+ const workoutItems: TimelineItem[] = [
1928
+ {
1929
+ id: '1', time: '11:35', endTime: '13:05', title: 'Cardio',
1930
+ meta: { iconName: 'heart', calories: '1200', bpm: '90', duration: '03:00' },
1931
+ },
1932
+ {
1933
+ id: '2', time: '14:45', endTime: '15:45', title: 'Muscle',
1934
+ meta: { iconName: 'zap', calories: '980', bpm: '102', duration: '01:00' },
1935
+ },
1936
+ ]
1937
+
1938
+ const WorkoutCard: React.FC<{ item: TimelineItem }> = ({ item }) => {
1939
+ const m = item.meta as unknown as WorkoutMeta
1940
+ return (
1941
+ <StyledCard padding={16} borderRadius={20} shadow="light" borderLeftWidth={4} borderLeftColor="#8bc34a">
1942
+ <StyledText fontSize={16} fontWeight="800">{item.title}</StyledText>
1943
+ <Stack horizontal gap={20} marginTop={8}>
1944
+ <StyledText fontSize={13} color="#6b7280">{m.calories} kcal</StyledText>
1945
+ <StyledText fontSize={13} color="#6b7280">{m.bpm} bpm</StyledText>
1946
+ <StyledText fontSize={13} color="#6b7280">{m.duration} hr</StyledText>
1947
+ </Stack>
1948
+ </StyledCard>
1949
+ )
1950
+ }
1951
+
1952
+ <StyledTimeline
1953
+ items={workoutItems}
1954
+ renderItem={(item) => <WorkoutCard item={item} />}
1955
+ variant="default"
1956
+ dotShape="filled"
1957
+ dotSize={10}
1958
+ timeColumnWidth={58}
1959
+ timeGap={12}
1960
+ animated
1961
+ colors={{ dot: '#8bc34a', line: '#e5e7eb', timeText: '#1a1a1e', endTimeText: '#9ca3af' }}
1962
+ />
1963
+
1964
+ // ── 4. Mixed: data items + appended children ──────────────────────────────────
1965
+ <StyledTimeline items={scheduleItems}>
1966
+ {/* This node is appended as a final timeline row */}
1967
+ <StyledCard padding={12}>
1968
+ <StyledText>Don't forget to hydrate! 💚</StyledText>
1969
+ </StyledCard>
1970
+ </StyledTimeline>
1971
+
1972
+ // ── 5. Density variants ───────────────────────────────────────────────────────
1973
+ <StyledTimeline items={items} variant="compact" />
1974
+ <StyledTimeline items={items} variant="default" />
1975
+ <StyledTimeline items={items} variant="spacious" />
1976
+
1977
+ // ── 6. Dot shapes ─────────────────────────────────────────────────────────────
1978
+ <StyledTimeline items={items} dotShape="filled" /> // solid dot (default)
1979
+ <StyledTimeline items={items} dotShape="ring" /> // white fill, coloured border
1980
+ <StyledTimeline items={items} dotShape="circle" /> // hollow ring
1981
+
1982
+ // ── 7. Blue theme with ring dots ─────────────────────────────────────────────
1983
+ <StyledTimeline
1984
+ items={items}
1985
+ variant="spacious"
1986
+ dotShape="ring"
1987
+ colors={{ dot: '#2196f3', line: '#bbdefb', dotBorder: '#fff' }}
1988
+ />
1989
+
1990
+ // ── 8. Press handler — navigate on tap ───────────────────────────────────────
1991
+ <StyledTimeline
1992
+ items={items}
1993
+ onItemPress={(item) => navigation.navigate('SessionDetail', { id: item.id })}
1994
+ />
1995
+
1996
+ // ── 9. Conditional rendering (rest day) ──────────────────────────────────────
1997
+ // items is [] for rest days — render an empty state instead
1998
+ {items.length > 0 ? (
1999
+ <StyledTimeline items={items} renderItem={(item) => <WorkoutCard item={item} />} />
2000
+ ) : (
2001
+ <Stack alignItems="center" paddingVertical={48}>
2002
+ <StyledText fontSize={18} fontWeight="800">Rest Day</StyledText>
2003
+ <StyledText fontSize={14} color="#9ca3af">Recovery is part of the plan.</StyledText>
2004
+ </Stack>
2005
+ )}
2006
+ ```
2007
+
2008
+ ---
2009
+
2010
+ ### StyledRadio / StyledRadioGroup
2011
+
2012
+ Production-ready radio button system with three sub-components and three layout variants. Supports controlled and uncontrolled modes, generic value types, animated dot transitions, and full colour overrides.
2013
+
2014
+ ```tsx
2015
+ import { StyledRadio, StyledRadioGroup } from 'fluent-styles'
2016
+ ```
2017
+
2018
+ #### Sub-components
2019
+
2020
+ | Component | Description |
2021
+ |---|---|
2022
+ | `StyledRadio` | Raw animated radio dot — for custom layouts |
2023
+ | `StyledRadioGroup` | Full managed group with `list`, `card`, and `boxed` variants |
2024
+
2025
+ #### Sizes (`RadioSize`)
2026
+
2027
+ | Size | Outer | Inner dot | Border |
2028
+ |---|---|---|---|
2029
+ | `sm` | 16 | 7 | 1.5 |
2030
+ | `md` | 20 | 9 | 2.0 |
2031
+ | `lg` | 24 | 11 | 2.5 |
2032
+
2033
+ #### Variants (`RadioVariant`)
2034
+
2035
+ | Variant | Description |
2036
+ |---|---|
2037
+ | `list` | Full-width bordered rows — each option is a standalone pressable card |
2038
+ | `card` | Horizontal grid (configurable columns) — compact cards for delivery/plan selection |
2039
+ | `boxed` | Single card wrapper with a title + divider-separated rows inside |
2040
+
2041
+ #### `RadioOption<T>`
2042
+
2043
+ | Field | Type | Description |
2044
+ |---|---|---|
2045
+ | `value` | `T` | Unique option value (string or number) |
2046
+ | `label` | `string` | Primary label text |
2047
+ | `subtitle` | `string` | Secondary description line |
2048
+ | `rightContent` | `ReactNode` | Content displayed on the right (price, tag, etc.) |
2049
+ | `leadingContent` | `ReactNode` | Leading content (logo, icon, etc.) |
2050
+ | `badge` | `ReactNode` | Inline badge after the label (e.g. `"SAVE 33%"`) |
2051
+ | `disabled` | `boolean` | Disables this specific option |
2052
+
2053
+ #### `StyledRadioColors`
2054
+
2055
+ | Field | Default | Description |
2056
+ |---|---|---|
2057
+ | `active` | `gray[900]` | Active dot and border colour |
2058
+ | `inactive` | `gray[300]` | Inactive ring colour |
2059
+ | `selectedCardBg` | `active + 5% opacity` | Selected item background |
2060
+ | `selectedCardBorder` | `active` | Selected item border |
2061
+ | `unselectedCardBorder` | `gray[200]` | Unselected item border |
2062
+ | `label` | `gray[900]` | Label text colour |
2063
+ | `subtitle` | `gray[400]` | Subtitle text colour |
2064
+
2065
+ #### `StyledRadioGroupProps<T>`
2066
+
2067
+ | Prop | Type | Default | Description |
2068
+ |---|---|---|---|
2069
+ | `options` | `RadioOption<T>[]` | — | Options to render |
2070
+ | `value` | `T` | — | Controlled selected value |
2071
+ | `defaultValue` | `T` | — | Initial value for uncontrolled mode |
2072
+ | `onChange` | `(value: T) => void` | — | Called when selection changes |
2073
+ | `variant` | `RadioVariant` | `'list'` | Layout variant |
2074
+ | `size` | `RadioSize` | `'md'` | Dot size preset |
2075
+ | `title` | `string` | — | Section title (shown in `boxed` variant) |
2076
+ | `colors` | `StyledRadioColors` | — | Colour overrides |
2077
+ | `columns` | `number` | `3` | Columns for `card` variant |
2078
+ | `gap` | `number` | `10` | Gap between cards in `card` variant |
2079
+
2080
+ #### Usage
2081
+
2082
+ ```tsx
2083
+ import React, { useState } from 'react'
2084
+ import { StyledRadioGroup, StyledRadio, StyledCard, Stack, StyledText, type RadioOption } from 'fluent-styles'
2085
+
2086
+ // ── Shared colour theme ───────────────────────────────────────────────────────
2087
+ const BLUE_COLORS = {
2088
+ active: '#2563eb',
2089
+ selectedCardBg: '#eff6ff',
2090
+ selectedCardBorder: '#2563eb',
2091
+ unselectedCardBorder: '#e5e7eb',
2092
+ }
2093
+
2094
+ // ── 1. Subscription plan — list variant with badge + price block ──────────────
2095
+ // badge and rightContent let you embed arbitrary nodes beside each option.
2096
+ const PriceBlock = ({ main, sub }: { main: string; sub: string }) => (
2097
+ <Stack alignItems="flex-end" gap={2}>
2098
+ <StyledText fontSize={14} fontWeight="600">{main}</StyledText>
2099
+ <StyledText fontSize={12} color="#9ca3af">{sub}</StyledText>
2100
+ </Stack>
2101
+ )
2102
+
2103
+ const SaveBadge = ({ label }: { label: string }) => (
2104
+ <Stack paddingHorizontal={8} paddingVertical={3} borderRadius={6} backgroundColor="#dcfce7">
2105
+ <StyledText fontSize={10} fontWeight="700" color="#16a34a">{label}</StyledText>
2106
+ </Stack>
2107
+ )
2108
+
2109
+ const [plan, setPlan] = useState('yearly')
2110
+
2111
+ <StyledRadioGroup
2112
+ options={[
2113
+ {
2114
+ value: 'yearly',
2115
+ label: 'Yearly',
2116
+ badge: <SaveBadge label="SAVE 33%" />,
2117
+ rightContent: <PriceBlock main="$19.99/month" sub="$240 billed yearly" />,
2118
+ },
2119
+ {
2120
+ value: 'monthly',
2121
+ label: 'Monthly',
2122
+ rightContent: <PriceBlock main="$24/month" sub="$24 billed monthly" />,
2123
+ },
2124
+ ]}
2125
+ value={plan}
2126
+ onChange={setPlan}
2127
+ variant="list"
2128
+ colors={BLUE_COLORS}
2129
+ />
2130
+
2131
+ // ── 2. Billing period — boxed variant inside a card ─────────────────────────
2132
+ // `title` renders a bold heading inside the card above the options.
2133
+ <StyledRadioGroup
2134
+ title="Billing Period"
2135
+ options={[
2136
+ { value: 'monthly', label: 'Monthly', rightContent: <StyledText>$9.99/month</StyledText> },
2137
+ { value: 'yearly', label: 'Yearly', rightContent: <StyledText>$12.99/month</StyledText> },
2138
+ ]}
2139
+ defaultValue="monthly"
2140
+ variant="boxed"
2141
+ />
2142
+
2143
+ // ── 3. Delivery method — card variant (3-column grid, blue accent) ────────────
2144
+ // Each option becomes a compact card; columns controls the grid width.
2145
+ <StyledRadioGroup
2146
+ options={[
2147
+ { value: 'standard', label: 'Standard', subtitle: '4–10 business days',
2148
+ rightContent: <StyledText fontWeight="600">$5.00</StyledText> },
2149
+ { value: 'express', label: 'Express', subtitle: '2–5 business days',
2150
+ rightContent: <StyledText fontWeight="600" color="#2563eb">$16.00</StyledText> },
2151
+ { value: 'superfast', label: 'Super Fast', subtitle: '1 business day',
2152
+ rightContent: <StyledText fontWeight="600">$25.00</StyledText> },
2153
+ ]}
2154
+ defaultValue="express"
2155
+ variant="card"
2156
+ columns={3}
2157
+ gap={10}
2158
+ colors={{ ...BLUE_COLORS, subtitle: '#2563eb' }}
2159
+ />
2160
+
2161
+ // ── 4. Payment method — list variant with leading card logos ──────────────────
2162
+ // leadingContent rows any node before the radio dot (logos, avatars, icons…).
2163
+ const VisaLogo = () => (
2164
+ <Stack width={40} height={24} borderRadius={4} backgroundColor="#1a1f71"
2165
+ alignItems="center" justifyContent="center">
2166
+ <StyledText fontSize={11} fontWeight="900" color="#fff">VISA</StyledText>
2167
+ </Stack>
2168
+ )
2169
+
2170
+ const MastercardLogo = () => (
2171
+ <Stack width={36} height={24} borderRadius={4} backgroundColor="#f4f4f4"
2172
+ alignItems="center" justifyContent="center">
2173
+ <Stack horizontal>
2174
+ <Stack width={14} height={14} borderRadius={7} backgroundColor="#eb001b" />
2175
+ <Stack width={14} height={14} borderRadius={7} backgroundColor="#f79e1b" style={{ marginLeft: -5 }} />
2176
+ </Stack>
2177
+ </Stack>
2178
+ )
2179
+
2180
+ <StyledRadioGroup
2181
+ options={[
2182
+ { value: 'mc8304', leadingContent: <MastercardLogo />, label: '**** 8304',
2183
+ subtitle: 'Last used: Mar 26, 2022' },
2184
+ { value: 'visa0123', leadingContent: <VisaLogo />, label: '**** 0123',
2185
+ subtitle: 'Never used' },
2186
+ ]}
2187
+ defaultValue="visa0123"
2188
+ variant="list"
2189
+ colors={BLUE_COLORS}
2190
+ />
2191
+
2192
+ // ── 5. Size variants — sm / md / lg ─────────────────────────────────────────
2193
+ // Size affects only the radio dot; overall row proportions stay the same.
2194
+ <Stack gap={14}>
2195
+ {(['sm', 'md', 'lg'] as const).map((size) => (
2196
+ <Stack key={size}>
2197
+ <StyledText fontSize={12} color="#9ca3af" marginBottom={8}>{size}</StyledText>
2198
+ <StyledRadioGroup
2199
+ options={[
2200
+ { value: 'a', label: 'Option A', rightContent: <StyledText>$5.00</StyledText> },
2201
+ { value: 'b', label: 'Option B', rightContent: <StyledText>$10.00</StyledText> },
2202
+ ]}
2203
+ defaultValue="a"
2204
+ variant="list"
2205
+ size={size}
2206
+ colors={BLUE_COLORS}
2207
+ />
2208
+ </Stack>
2209
+ ))}
2210
+ </Stack>
2211
+
2212
+ // ── 6. Disabled individual options ───────────────────────────────────────────
2213
+ // Set disabled: true on any RadioOption to grey it out and block interaction.
2214
+ <StyledRadioGroup
2215
+ options={[
2216
+ { value: 'active', label: 'Active option', rightContent: <StyledText>$9.99</StyledText> },
2217
+ { value: 'disabled', label: 'Disabled option', rightContent: <StyledText>$19.99</StyledText>, disabled: true },
2218
+ { value: 'other', label: 'Another option', rightContent: <StyledText>$5.99</StyledText> },
2219
+ ]}
2220
+ defaultValue="active"
2221
+ variant="list"
2222
+ colors={BLUE_COLORS}
2223
+ />
2224
+
2225
+ // ── 7. StyledRadio standalone — custom layout swatches ───────────────────────
2226
+ // Use StyledRadio directly when you need the dot inside your own layout.
2227
+ <StyledCard padding={16} borderRadius={14} shadow="light">
2228
+ <Stack gap={16}>
2229
+ {[
2230
+ { label: 'Selected · dark', selected: true, color: '#111827' },
2231
+ { label: 'Unselected', selected: false, color: '#111827' },
2232
+ { label: 'Selected · blue', selected: true, color: '#2563eb' },
2233
+ { label: 'Selected · green', selected: true, color: '#16a34a' },
2234
+ { label: 'Selected · rose', selected: true, color: '#e11d48' },
2235
+ ].map(({ label, selected, color }) => (
2236
+ <Stack key={label} horizontal alignItems="center" gap={12}>
2237
+ <StyledRadio selected={selected} color={color} size="md" />
2238
+ <StyledText fontSize={14} color="#374151">{label}</StyledText>
2239
+ </Stack>
2240
+ ))}
2241
+ </Stack>
2242
+ </StyledCard>
2243
+ ```
2244
+
2245
+ ---
2246
+
2247
+ ### StyledProgressBar
2248
+
2249
+ Animated progress bar with 5 variants, 5 size presets, 3 shapes, 5 label positions, and full colour overrides. Backed by `react-native-svg` for gradient and striped fills.
2250
+
2251
+ ```tsx
2252
+ import { StyledProgressBar } from 'fluent-styles'
2253
+ ```
2254
+
2255
+ #### Variants
2256
+
2257
+ | Variant | Description |
2258
+ |---|---|
2259
+ | `default` | Flat filled bar |
2260
+ | `striped` | Diagonal animated stripe overlay |
2261
+ | `gradient` | Left-to-right colour gradient (SVG) |
2262
+ | `segmented` | Divided into N equal tick segments |
2263
+ | `buffer` | Primary fill + secondary buffer track (media player style) |
2264
+
2265
+ #### Sizes
2266
+
2267
+ | Size | Height |
2268
+ |---|---|
2269
+ | `xs` | 3 px |
2270
+ | `sm` | 6 px |
2271
+ | `md` | 10 px (default) |
2272
+ | `lg` | 16 px |
2273
+ | `xl` | 24 px |
2274
+
2275
+ #### Shapes
2276
+
2277
+ | Shape | Border radius |
2278
+ |---|---|
2279
+ | `rounded` | `height / 2` (default) |
2280
+ | `square` | `0` |
2281
+ | `pill` | `999` |
2282
+
2283
+ #### Label positions
2284
+
2285
+ | Position | Placement |
2286
+ |---|---|
2287
+ | `none` | Hidden (default) |
2288
+ | `above` | Right-aligned above the bar |
2289
+ | `below` | Right-aligned below the bar |
2290
+ | `right` | Inline to the right of the bar |
2291
+ | `inside` | Centred inside the filled bar (requires `lg` or `xl`) |
2292
+
2293
+ #### `StyledProgressColors`
2294
+
2295
+ | Field | Default | Description |
2296
+ |---|---|---|
2297
+ | `fill` | `blue[500]` | Filled track colour |
2298
+ | `track` | `gray[100]` | Background track colour |
2299
+ | `buffer` | `gray[300]` | Buffer layer colour (`buffer` variant) |
2300
+ | `stripe` | `rgba(255,255,255,0.25)` | Stripe overlay (`striped` variant) |
2301
+ | `gradFrom` | `blue[400]` | Gradient start (`gradient` variant) |
2302
+ | `gradTo` | `indigo[600]` | Gradient end (`gradient` variant) |
2303
+ | `label` | `gray[700]` | External label text colour |
2304
+ | `labelInside` | `white` | Inside label colour |
2305
+
2306
+ #### Props
2307
+
2308
+ | Prop | Type | Default | Description |
2309
+ |---|---|---|---|
2310
+ | `value` | `number` | required | Current progress (0–`total`) |
2311
+ | `total` | `number` | `100` | Maximum value |
2312
+ | `bufferValue` | `number` | = `value` | Buffer position (`buffer` variant) |
2313
+ | `variant` | `ProgressVariant` | `'default'` | Visual style |
2314
+ | `size` | `ProgressSize` | `'md'` | Height preset |
2315
+ | `shape` | `ProgressShape` | `'rounded'` | Bar end shape |
2316
+ | `labelPosition` | `LabelPosition` | `'none'` | Where to show the percentage |
2317
+ | `label` | `string \| false` | auto `%` | Custom label; `false` hides it |
2318
+ | `showSteps` | `boolean` | `false` | Show `value / total` instead of `%` |
2319
+ | `segments` | `number` | `5` | Segment count (`segmented` variant) |
2320
+ | `segmentGap` | `number` | `3` | Gap between segments in px |
2321
+ | `width` | `number` | container width | Explicit pixel width |
2322
+ | `animated` | `boolean` | `true` | Animate fill on mount / value change |
2323
+ | `animationDuration` | `number` | `600` | Animation duration in ms |
2324
+ | `colors` | `StyledProgressColors` | blue theme | Colour overrides |
2325
+ | `onAnimationComplete` | `() => void` | — | Fires when animation finishes |
2326
+
2327
+ #### Usage
2328
+
2329
+ ```tsx
2330
+ import { StyledProgressBar } from 'fluent-styles'
2331
+
2332
+ // ── 1. Variants ───────────────────────────────────────────────────────────────
2333
+ <StyledProgressBar value={65} labelPosition="right" />
2334
+
2335
+ <StyledProgressBar value={45} variant="striped" size="lg" labelPosition="right" />
2336
+
2337
+ <StyledProgressBar value={72} variant="gradient" labelPosition="right"
2338
+ colors={{ gradFrom: '#6366f1', gradTo: '#22d3ee' }} />
2339
+
2340
+ // Segmented — workout sets: 5 of 9 complete
2341
+ <StyledProgressBar
2342
+ value={5}
2343
+ total={9}
2344
+ variant="segmented"
2345
+ segments={9}
2346
+ showSteps
2347
+ labelPosition="right"
2348
+ colors={{ fill: '#8bc34a', track: '#e5e7eb' }}
2349
+ />
2350
+
2351
+ // Buffer — media player (loaded 60%, played 30%)
2352
+ <StyledProgressBar
2353
+ value={30}
2354
+ bufferValue={60}
2355
+ variant="buffer"
2356
+ size="sm"
2357
+ labelPosition="right"
2358
+ colors={{ fill: '#2563eb', buffer: '#bfdbfe', track: '#e5e7eb' }}
2359
+ />
2360
+
2361
+ // ── 2. Sizes side-by-side ─────────────────────────────────────────────────────
2362
+ {(['xs', 'sm', 'md', 'lg', 'xl'] as const).map((s) => (
2363
+ <StyledProgressBar key={s} value={65} size={s} labelPosition="right"
2364
+ colors={{ fill: '#3b82f6' }} />
2365
+ ))}
2366
+
2367
+ // ── 3. Label positions ────────────────────────────────────────────────────────
2368
+ <StyledProgressBar value={60} labelPosition="above" />
2369
+ <StyledProgressBar value={60} labelPosition="below" />
2370
+ <StyledProgressBar value={60} labelPosition="right" />
2371
+ // Inside label requires lg or xl
2372
+ <StyledProgressBar value={60} size="lg" labelPosition="inside"
2373
+ colors={{ fill: '#3b82f6', labelInside: '#fff' }} />
2374
+ <StyledProgressBar value={55} size="xl" variant="striped" labelPosition="inside"
2375
+ colors={{ fill: '#8bc34a', labelInside: '#1a1a1a' }} />
2376
+
2377
+ // ── 4. Shapes ─────────────────────────────────────────────────────────────────
2378
+ <StyledProgressBar value={65} size="lg" shape="rounded" labelPosition="right" />
2379
+ <StyledProgressBar value={65} size="lg" shape="square" labelPosition="right" />
2380
+ <StyledProgressBar value={65} size="lg" shape="pill" labelPosition="right" />
2381
+
2382
+ // ── 5. Colour themes ─────────────────────────────────────────────────────────
2383
+ <StyledProgressBar value={65} size="md" labelPosition="right" colors={{ fill: '#3b82f6', track: '#dbeafe' }} />
2384
+ <StyledProgressBar value={65} size="md" labelPosition="right" colors={{ fill: '#8bc34a', track: '#ecfccb' }} />
2385
+ <StyledProgressBar value={65} size="md" labelPosition="right" colors={{ fill: '#f43f5e', track: '#ffe4e6' }} />
2386
+ <StyledProgressBar value={65} size="md" labelPosition="right" colors={{ fill: '#f59e0b', track: '#fef3c7' }} />
2387
+
2388
+ // ── 6. Gradient themes ────────────────────────────────────────────────────────
2389
+ {[
2390
+ { label: 'Indigo → Cyan', from: '#6366f1', to: '#22d3ee' },
2391
+ { label: 'Rose → Orange', from: '#f43f5e', to: '#fb923c' },
2392
+ { label: 'Lime → Emerald', from: '#a3e635', to: '#10b981' },
2393
+ { label: 'Violet → Pink', from: '#8b5cf6', to: '#ec4899' },
2394
+ ].map(({ from, to }) => (
2395
+ <StyledProgressBar value={70} variant="gradient" size="md" labelPosition="right"
2396
+ colors={{ gradFrom: from, gradTo: to }} />
2397
+ ))}
2398
+
2399
+ // ── 7. Controlled with button strip ──────────────────────────────────────────
2400
+ const [progress, setProgress] = useState(45)
2401
+
2402
+ <StyledProgressBar value={progress} variant="gradient" size="lg" labelPosition="above"
2403
+ animationDuration={400}
2404
+ colors={{ gradFrom: '#6366f1', gradTo: '#22d3ee' }} />
2405
+
2406
+ <Stack horizontal gap={10} justifyContent="center">
2407
+ {[0, 25, 50, 75, 100].map((v) => (
2408
+ <StyledPressable key={v} onPress={() => setProgress(v)}
2409
+ paddingHorizontal={14} paddingVertical={8} borderRadius={20}
2410
+ backgroundColor={progress === v ? '#6366f1' : '#f3f4f6'}>
2411
+ <StyledText fontSize={13} fontWeight="600"
2412
+ color={progress === v ? '#fff' : '#374151'}>{v}%</StyledText>
2413
+ </StyledPressable>
2414
+ ))}
2415
+ </Stack>
2416
+
2417
+ // ── 8. Real-world: workout card ───────────────────────────────────────────────
2418
+ {[
2419
+ { title: 'Cardio', progress: 65, label: '4 Of 6', color: '#dc2626', bg: '#fff0f0' },
2420
+ { title: 'Muscle', progress: 62, label: '5 Of 8', color: '#9333ea', bg: '#fdf4ff' },
2421
+ { title: 'Weight', progress: 44, label: '4 Of 9', color: '#ea580c', bg: '#fff7ed' },
2422
+ ].map(({ title, progress, label, color, bg }) => (
2423
+ <Stack key={title} marginBottom={16}>
2424
+ <Stack horizontal alignItems="center" gap={10} marginBottom={8}>
2425
+ <Stack width={32} height={32} borderRadius={16} backgroundColor={bg}
2426
+ alignItems="center" justifyContent="center">
2427
+ <Icon name="activity" size={14} color={color} />
2428
+ </Stack>
2429
+ <StyledText fontSize={14} fontWeight="700">{title}</StyledText>
2430
+ </Stack>
2431
+ <StyledProgressBar
2432
+ value={progress}
2433
+ size="sm"
2434
+ labelPosition="right"
2435
+ label={label}
2436
+ colors={{ fill: color, track: '#f3f4f6' }}
2437
+ />
2438
+ </Stack>
2439
+ ))}
2440
+
2441
+ // ── 9. Real-world: file upload status ─────────────────────────────────────────
2442
+ {[
2443
+ { name: 'design-assets.zip', size: '24.5 MB', value: 100, done: true },
2444
+ { name: 'report-final.pdf', size: '3.2 MB', value: 67, done: false },
2445
+ { name: 'video-export.mp4', size: '128 MB', value: 23, done: false },
2446
+ ].map(({ name, size, value, done }) => (
2447
+ <Stack key={name} marginBottom={14}>
2448
+ <Stack horizontal alignItems="center" justifyContent="space-between" marginBottom={6}>
2449
+ <StyledText fontSize={13} fontWeight="600">{name}</StyledText>
2450
+ <StyledText fontSize={11} color="#9ca3af">{size}</StyledText>
2451
+ </Stack>
2452
+ <StyledProgressBar
2453
+ value={value}
2454
+ size="xs"
2455
+ shape="pill"
2456
+ colors={{ fill: done ? '#10b981' : '#3b82f6', track: '#f3f4f6' }}
2457
+ />
2458
+ </Stack>
2459
+ ))}
2460
+
2461
+ // ── 10. Real-world: skills profile ────────────────────────────────────────────
2462
+ {[
2463
+ { skill: 'React Native', pct: 92 },
2464
+ { skill: 'TypeScript', pct: 85 },
2465
+ { skill: 'UI Design', pct: 74 },
2466
+ ].map(({ skill, pct }) => (
2467
+ <Stack key={skill} horizontal alignItems="center" gap={12} marginBottom={12}>
2468
+ <StyledText fontSize={13} fontWeight="600" width={100}>{skill}</StyledText>
2469
+ <Stack flex={1}>
2470
+ <StyledProgressBar value={pct} size="md" variant="gradient" labelPosition="right"
2471
+ colors={{ gradFrom: '#6366f1', gradTo: '#8b5cf6' }} />
2472
+ </Stack>
2473
+ </Stack>
2474
+ ))}
2475
+ ```
2476
+
2477
+ ---
2478
+
2479
+ ### StyledSlider
2480
+
2481
+ Gesture-driven slider with PanResponder, animated thumb scale, tooltip, tick marks, and 5 variants. Range mode manages two independent thumbs. Backed by `react-native-svg` for gradient fills.
2482
+
2483
+ ```tsx
2484
+ import { StyledSlider } from 'fluent-styles'
2485
+ ```
2486
+
2487
+ #### Variants
2488
+
2489
+ | Variant | Description |
2490
+ |---|---|
2491
+ | `default` | Single thumb, fill left of thumb |
2492
+ | `range` | Two thumbs, fill between them |
2493
+ | `stepped` | Snaps to discrete tick marks |
2494
+ | `gradient` | Gradient-filled track |
2495
+ | `buffer` | Primary thumb + secondary buffer fill (media player style) |
2496
+
2497
+ #### Sizes
2498
+
2499
+ | Size | Track height | Thumb diameter |
2500
+ |---|---|---|
2501
+ | `sm` | 4 px | 18 px |
2502
+ | `md` | 6 px (default) | 24 px |
2503
+ | `lg` | 10 px | 32 px |
2504
+
2505
+ #### `StyledSliderColors`
2506
+
2507
+ | Field | Default | Description |
2508
+ |---|---|---|
2509
+ | `fill` | `#3b82f6` | Filled track colour |
2510
+ | `track` | `gray[200]` | Background track |
2511
+ | `buffer` | `gray[300]` | Buffer fill (`buffer` variant) |
2512
+ | `thumb` | `white` | Thumb fill |
2513
+ | `thumbBorder` | = `fill` | Thumb border colour |
2514
+ | `gradFrom` | `#60a5fa` | Gradient start (`gradient` variant) |
2515
+ | `gradTo` | `#4f46e5` | Gradient end (`gradient` variant) |
2516
+ | `tooltipBg` | `#111827` | Tooltip background |
2517
+ | `tooltipText` | `white` | Tooltip text |
2518
+ | `rangeLabel` | `gray[400]` | Min/max label colour |
2519
+ | `tick` | `gray[300]` | Inactive tick colour |
2520
+ | `tickActive` | = `fill` | Filled tick colour |
2521
+
2522
+ #### Props
2523
+
2524
+ | Prop | Type | Default | Description |
2525
+ |---|---|---|---|
2526
+ | `value` | `number` | required | Current thumb value (or low thumb for `range`) |
2527
+ | `valueHigh` | `number` | — | High thumb value (`range` variant) |
2528
+ | `bufferValue` | `number` | — | Buffer position (`buffer` variant) |
2529
+ | `min` | `number` | `0` | Minimum value |
2530
+ | `max` | `number` | `100` | Maximum value |
2531
+ | `step` | `number` | `1` | Step increment |
2532
+ | `variant` | `SliderVariant` | `'default'` | Visual style |
2533
+ | `size` | `SliderSize` | `'md'` | Track/thumb size preset |
2534
+ | `showTooltip` | `boolean` | `true` | Show tooltip while dragging |
2535
+ | `alwaysShowTooltip` | `boolean` | `false` | Keep tooltip permanently visible |
2536
+ | `showMinMax` | `boolean` | `false` | Display min/max labels at track ends |
2537
+ | `steps` | `number` | `5` | Number of ticks (`stepped` variant) |
2538
+ | `formatLabel` | `(v: number) => string` | `String(v)` | Custom tooltip/tick label formatter |
2539
+ | `width` | `number` | container width | Explicit pixel width |
2540
+ | `disabled` | `boolean` | `false` | Disables interaction, reduces opacity |
2541
+ | `colors` | `StyledSliderColors` | blue theme | Colour overrides |
2542
+ | `onValueChange` | `(value: number) => void` | — | Fires continuously while dragging |
2543
+ | `onSlidingComplete` | `(value: number) => void` | — | Fires once on drag release |
2544
+ | `onRangeChange` | `(low, high: number) => void` | — | Range drag callback |
2545
+ | `onRangeComplete` | `(low, high: number) => void` | — | Range drag-release callback |
2546
+
2547
+ #### Usage
2548
+
2549
+ ```tsx
2550
+ import React, { useState } from 'react'
2551
+ import { StyledSlider, Stack, StyledText } from 'fluent-styles'
2552
+ import Icon from 'react-native-vector-icons/Feather'
2553
+
2554
+ // ── 1. Default — basic volume control ────────────────────────────────────────
2555
+ const [vol, setVol] = useState(65)
2556
+
2557
+ <StyledSlider value={vol} onValueChange={setVol} showMinMax />
2558
+
2559
+ // ── 2. Range — price filter ───────────────────────────────────────────────────
2560
+ // valueHigh is required for the range variant.
2561
+ const [priceLow, setPriceLow] = useState(20)
2562
+ const [priceHigh, setPriceHigh] = useState(75)
2563
+
2564
+ <StyledSlider
2565
+ variant="range"
2566
+ value={priceLow}
2567
+ valueHigh={priceHigh}
2568
+ min={0}
2569
+ max={200}
2570
+ step={5}
2571
+ onRangeChange={(lo, hi) => { setPriceLow(lo); setPriceHigh(hi) }}
2572
+ showMinMax
2573
+ formatLabel={(v) => `$${v}`}
2574
+ colors={{ fill: '#6366f1', thumbBorder: '#6366f1', tooltipBg: '#6366f1' }}
2575
+ />
2576
+
2577
+ // ── 3. Stepped — star rating (snaps to 5 ticks) ───────────────────────────────
2578
+ // steps controls both the number of ticks and the snap resolution.
2579
+ const [rating, setRating] = useState(4)
2580
+
2581
+ <StyledSlider
2582
+ variant="stepped"
2583
+ value={rating}
2584
+ min={1} max={5} steps={5}
2585
+ onValueChange={setRating}
2586
+ alwaysShowTooltip
2587
+ size="lg"
2588
+ formatLabel={(v) => ['','★','★★','★★★','★★★★','★★★★★'][Math.round(v)] ?? ''}
2589
+ colors={{
2590
+ fill: '#f59e0b',
2591
+ track: '#fef3c7',
2592
+ thumbBorder: '#f59e0b',
2593
+ tooltipBg: '#f59e0b',
2594
+ tickActive: '#f59e0b',
2595
+ }}
2596
+ />
2597
+
2598
+ // ── 4. Gradient — temperature control ────────────────────────────────────────
2599
+ const [temp, setTemp] = useState(22)
2600
+
2601
+ <StyledSlider
2602
+ variant="gradient"
2603
+ value={temp}
2604
+ min={10} max={35}
2605
+ onValueChange={setTemp}
2606
+ formatLabel={(v) => `${v}°`}
2607
+ alwaysShowTooltip
2608
+ showMinMax
2609
+ size="lg"
2610
+ colors={{
2611
+ gradFrom: '#60a5fa',
2612
+ gradTo: '#ef4444',
2613
+ tooltipBg: temp < 22 ? '#60a5fa' : '#ef4444',
2614
+ }}
2615
+ />
2616
+
2617
+ // ── 5. Buffer — media player seek bar ────────────────────────────────────────
2618
+ const formatTime = (s: number) =>
2619
+ `${Math.floor(s / 60)}:${String(Math.floor(s % 60)).padStart(2, '0')}`
2620
+
2621
+ const [played, setPlayed] = useState(95)
2622
+ const DURATION = 243
2623
+
2624
+ <StyledSlider
2625
+ variant="buffer"
2626
+ value={played}
2627
+ bufferValue={Math.min(played + 60, DURATION)}
2628
+ min={0}
2629
+ max={DURATION}
2630
+ size="sm"
2631
+ onValueChange={setPlayed}
2632
+ formatLabel={formatTime}
2633
+ colors={{ fill: '#2563eb', buffer: '#bfdbfe' }}
2634
+ />
2635
+
2636
+ // ── 6. Sizes ──────────────────────────────────────────────────────────────────
2637
+ <StyledSlider value={40} size="sm" onValueChange={() => {}} />
2638
+ <StyledSlider value={60} size="md" onValueChange={() => {}} />
2639
+ <StyledSlider value={80} size="lg" onValueChange={() => {}} />
2640
+
2641
+ // ── 7. Colour themes ──────────────────────────────────────────────────────────
2642
+ {[
2643
+ { fill: '#8bc34a', track: '#ecfccb', label: 'Lime' },
2644
+ { fill: '#f43f5e', track: '#ffe4e6', label: 'Rose' },
2645
+ { fill: '#f59e0b', track: '#fef3c7', label: 'Amber' },
2646
+ { fill: '#8b5cf6', track: '#ede9fe', label: 'Purple' },
2647
+ { fill: '#14b8a6', track: '#ccfbf1', label: 'Teal' },
2648
+ ].map(({ fill, track }) => (
2649
+ <StyledSlider
2650
+ value={65}
2651
+ onValueChange={() => {}}
2652
+ colors={{ fill, track, thumbBorder: fill, tooltipBg: fill }}
2653
+ formatLabel={(v) => `${v}%`}
2654
+ />
2655
+ ))}
2656
+
2657
+ // ── 8. Real-world: Audio / brightness controls with icons ─────────────────────
2658
+ <Stack horizontal alignItems="center" gap={12}>
2659
+ <Icon name="volume-x" size={18} color="#9ca3af" />
2660
+ <Stack flex={1}>
2661
+ <StyledSlider value={vol} onValueChange={setVol} showTooltip={false}
2662
+ colors={{ fill: '#1a1a1a', track: '#e5e7eb', thumbBorder: '#1a1a1a' }} />
2663
+ </Stack>
2664
+ <Icon name="volume-2" size={18} color="#9ca3af" />
2665
+ <StyledText fontSize={13} fontWeight="600" width={32}>{vol}%</StyledText>
2666
+ </Stack>
2667
+
2668
+ <Stack horizontal alignItems="center" gap={12}>
2669
+ <Icon name="sun" size={18} color="#9ca3af" />
2670
+ <Stack flex={1}>
2671
+ <StyledSlider value={bright} onValueChange={setBright} showTooltip={false}
2672
+ colors={{ fill: '#f59e0b', track: '#fef3c7', thumbBorder: '#f59e0b' }} />
2673
+ </Stack>
2674
+ <Icon name="sun" size={18} color="#f59e0b" />
2675
+ <StyledText fontSize={13} fontWeight="600" width={32}>{bright}%</StyledText>
2676
+ </Stack>
2677
+
2678
+ // ── 9. Disabled state ─────────────────────────────────────────────────────────
2679
+ <StyledSlider value={55} disabled showMinMax />
2680
+ ```
2681
+
2682
+ ---
2683
+
2684
+ ### StyledDatePicker
2685
+
2686
+ Pure-JS date / time picker — no native dependencies. Built entirely on fluent-styles primitives. Supports 5 modes, 3 display variants, scroll-drum time selection, month grid, date range, `minDate` / `maxDate` guards, and full colour overrides.
2687
+
2688
+ ```tsx
2689
+ import { StyledDatePicker } from 'fluent-styles'
2690
+ ```
2691
+
2692
+ #### Modes
2693
+
2694
+ | Mode | Description |
2695
+ |---|---|
2696
+ | `date` | Calendar grid — pick a single day (default) |
2697
+ | `time` | Scroll-drum hour / minute / AM-PM picker |
2698
+ | `datetime` | Calendar grid + time drum combined |
2699
+ | `range` | Two-tap calendar — start then end date |
2700
+ | `month` | Month grid + year scroll drum |
2701
+
2702
+ #### Variants
2703
+
2704
+ | Variant | Description |
2705
+ |---|---|
2706
+ | `inline` | Always-visible calendar embedded in the layout (default) |
2707
+ | `sheet` | Picker slides up in a Modal bottom-sheet |
2708
+ | `input` | Tappable input field that opens the sheet |
2709
+
2710
+ #### Sizes
2711
+
2712
+ | Size | Day-cell size | Font |
2713
+ |---|---|---|
2714
+ | `sm` | 34 px | 13 px |
2715
+ | `md` | 40 px (default) | 15 px |
2716
+ | `lg` | 48 px | 17 px |
2717
+
2718
+ #### `StyledDatePickerColors`
2719
+
2720
+ | Field | Default | Description |
2721
+ |---|---|---|
2722
+ | `selected` | `gray[900]` | Selected day circle fill |
2723
+ | `selectedText` | `white` | Selected day text |
2724
+ | `today` | `#3b82f6` | Today ring / text colour |
2725
+ | `rangeFill` | `selected + 9% alpha` | Fill between range start/end |
2726
+ | `dayText` | `gray[800]` | Regular day text |
2727
+ | `disabledText` | `gray[300]` | Out-of-range day text |
2728
+ | `headerText` | `gray[900]` | Month/year header text |
2729
+ | `background` | `white` | Sheet / inline background |
2730
+ | `inputBorder` | `gray[200]` | Input field border |
2731
+ | `confirmBg` | `gray[900]` | Confirm button background |
2732
+ | `confirmText` | `white` | Confirm button text |
2733
+
2734
+ #### Props
2735
+
2736
+ | Prop | Type | Default | Description |
2737
+ |---|---|---|---|
2738
+ | `mode` | `DatePickerMode` | `'date'` | Calendar mode |
2739
+ | `variant` | `DatePickerVariant` | `'inline'` | Display variant |
2740
+ | `size` | `DatePickerSize` | `'md'` | Day-cell size preset |
2741
+ | `value` | `Date \| null` | — | Selected date (`date` / `time` / `datetime` / `month`) |
2742
+ | `valueStart` | `Date \| null` | — | Range start date (`range` mode) |
2743
+ | `valueEnd` | `Date \| null` | — | Range end date (`range` mode) |
2744
+ | `minDate` | `Date` | — | Earliest selectable date |
2745
+ | `maxDate` | `Date` | — | Latest selectable date |
2746
+ | `showTodayButton` | `boolean` | `true` | Show "Today" shortcut button |
2747
+ | `showConfirm` | `boolean` | `true` | Show Confirm button in sheet |
2748
+ | `confirmLabel` | `string` | `'Done'` | Confirm button label |
2749
+ | `placeholder` | `string` | — | Input variant placeholder text |
2750
+ | `label` | `string` | — | Input variant field label |
2751
+ | `formatDisplay` | `(date: Date) => string` | auto | Custom input display formatter |
2752
+ | `colors` | `StyledDatePickerColors` | dark theme | Colour overrides |
2753
+ | `onChange` | `(date: Date) => void` | — | Fires on every date selection |
2754
+ | `onRangeChange` | `(start, end: Date\|null) => void` | — | Fires when either range thumb changes |
2755
+ | `onConfirm` | `(date: Date\|null) => void` | — | Fires when Confirm is tapped |
2756
+ | `disabled` | `boolean` | `false` | Disables all interaction |
2757
+
2758
+ #### Usage
2759
+
2760
+ ```tsx
2761
+ import React, { useState } from 'react'
2762
+ import { StyledDatePicker } from 'fluent-styles'
2763
+
2764
+ // ── 1. Date — always-visible inline calendar ──────────────────────────────────
2765
+ const [date, setDate] = useState<Date | null>(new Date())
2766
+
2767
+ <StyledDatePicker
2768
+ mode="date"
2769
+ variant="inline"
2770
+ value={date}
2771
+ onChange={setDate}
2772
+ showTodayButton
2773
+ />
2774
+
2775
+ // ── 2. Date — input field that opens a bottom-sheet ──────────────────────────
2776
+ const [apptDate, setApptDate] = useState<Date | null>(null)
2777
+
2778
+ <StyledDatePicker
2779
+ mode="date"
2780
+ variant="input"
2781
+ label="Appointment date"
2782
+ placeholder="Pick a date"
2783
+ value={apptDate}
2784
+ onChange={setApptDate}
2785
+ onConfirm={setApptDate}
2786
+ confirmLabel="Confirm date"
2787
+ />
2788
+
2789
+ // ── 3. Date range — inline with indigo fill ───────────────────────────────────
2790
+ // Tap once for start, tap again for end.
2791
+ const [rangeS, setRangeS] = useState<Date | null>(null)
2792
+ const [rangeE, setRangeE] = useState<Date | null>(null)
2793
+
2794
+ <StyledDatePicker
2795
+ mode="range"
2796
+ variant="inline"
2797
+ valueStart={rangeS}
2798
+ valueEnd={rangeE}
2799
+ onRangeChange={(s, e) => { setRangeS(s); setRangeE(e) }}
2800
+ colors={{ selected: '#6366f1', rangeFill: '#eef2ff', today: '#6366f1' }}
2801
+ />
2802
+
2803
+ // ── 4. Time — scroll drum ─────────────────────────────────────────────────────
2804
+ const [time, setTime] = useState<Date | null>(new Date())
2805
+
2806
+ <StyledDatePicker
2807
+ mode="time"
2808
+ variant="inline"
2809
+ value={time}
2810
+ onChange={setTime}
2811
+ showTodayButton={false}
2812
+ />
2813
+
2814
+ // ── 5. Date + Time combined ───────────────────────────────────────────────────
2815
+ const [dt, setDt] = useState<Date | null>(new Date())
2816
+
2817
+ <StyledDatePicker
2818
+ mode="datetime"
2819
+ variant="inline"
2820
+ value={dt}
2821
+ onChange={setDt}
2822
+ colors={{ selected: '#0f172a', today: '#6366f1' }}
2823
+ />
2824
+
2825
+ // ── 6. Month picker ───────────────────────────────────────────────────────────
2826
+ const [month, setMonth] = useState<Date | null>(new Date())
2827
+
2828
+ <StyledDatePicker
2829
+ mode="month"
2830
+ variant="inline"
2831
+ value={month}
2832
+ onChange={setMonth}
2833
+ colors={{ selected: '#059669', today: '#059669' }}
2834
+ />
2835
+
2836
+ // ── 7. Colour themes ──────────────────────────────────────────────────────────
2837
+ // Lime
2838
+ <StyledDatePicker mode="date" variant="inline" value={lime} onChange={setLime}
2839
+ colors={{ selected: '#8bc34a', today: '#8bc34a', confirmBg: '#8bc34a' }} />
2840
+
2841
+ // Rose
2842
+ <StyledDatePicker mode="date" variant="inline" value={rose} onChange={setRose}
2843
+ colors={{ selected: '#e11d48', today: '#e11d48', rangeFill: '#fff1f2' }} />
2844
+
2845
+ // Indigo
2846
+ <StyledDatePicker mode="date" variant="inline" value={indigo} onChange={setIndigo}
2847
+ colors={{ selected: '#6366f1', today: '#6366f1', rangeFill: '#eef2ff' }} />
2848
+
2849
+ // Amber
2850
+ <StyledDatePicker mode="date" variant="inline" value={amber} onChange={setAmber}
2851
+ colors={{ selected: '#d97706', today: '#d97706', confirmBg: '#d97706' }} />
2852
+
2853
+ // ── 8. Real-world: Hotel booking ──────────────────────────────────────────────
2854
+ // Two input pickers; check-out minDate is bound to check-in.
2855
+ const [checkIn, setCheckIn] = useState<Date | null>(null)
2856
+ const [checkOut, setCheckOut] = useState<Date | null>(null)
2857
+
2858
+ <StyledDatePicker
2859
+ mode="date" variant="input"
2860
+ label="Check-in" placeholder="Select check-in"
2861
+ value={checkIn}
2862
+ onChange={setCheckIn}
2863
+ onConfirm={setCheckIn}
2864
+ minDate={new Date()}
2865
+ colors={{ selected: '#0ea5e9', today: '#0ea5e9', confirmBg: '#0ea5e9' }}
2866
+ />
2867
+ <StyledDatePicker
2868
+ mode="date" variant="input"
2869
+ label="Check-out" placeholder="Select check-out"
2870
+ value={checkOut}
2871
+ onChange={setCheckOut}
2872
+ onConfirm={setCheckOut}
2873
+ minDate={checkIn ?? new Date()}
2874
+ colors={{ selected: '#0ea5e9', today: '#0ea5e9', confirmBg: '#0ea5e9' }}
2875
+ />
2876
+
2877
+ // ── 9. Real-world: Event scheduler ───────────────────────────────────────────
2878
+ // Separate date + time inputs, purple theme.
2879
+ const [eventDate, setEventDate] = useState<Date | null>(null)
2880
+ const [eventTime, setEventTime] = useState<Date | null>(null)
2881
+
2882
+ <StyledDatePicker
2883
+ mode="date" variant="input"
2884
+ label="Event date" placeholder="Choose date"
2885
+ value={eventDate} onChange={setEventDate} onConfirm={setEventDate}
2886
+ colors={{ selected: '#8b5cf6', today: '#8b5cf6', confirmBg: '#8b5cf6' }}
2887
+ />
2888
+ <StyledDatePicker
2889
+ mode="time" variant="input"
2890
+ label="Event time" placeholder="Choose time"
2891
+ value={eventTime} onChange={setEventTime} onConfirm={setEventTime}
2892
+ formatDisplay={(d) => d.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })}
2893
+ colors={{ selected: '#8b5cf6', today: '#8b5cf6', confirmBg: '#8b5cf6' }}
2894
+ />
2895
+
2896
+ // ── 10. Real-world: Report period ────────────────────────────────────────────
2897
+ // Month-mode input pickers with amber theme.
2898
+ const [reportFrom, setReportFrom] = useState<Date | null>(null)
2899
+ const [reportTo, setReportTo] = useState<Date | null>(null)
2900
+
2901
+ <StyledDatePicker
2902
+ mode="month" variant="input"
2903
+ label="From month" placeholder="Select start month"
2904
+ value={reportFrom} onChange={setReportFrom} onConfirm={setReportFrom}
2905
+ formatDisplay={(d) => d.toLocaleDateString('en-US', { month: 'long', year: 'numeric' })}
2906
+ colors={{ selected: '#f59e0b', today: '#f59e0b', confirmBg: '#f59e0b' }}
2907
+ />
2908
+ <StyledDatePicker
2909
+ mode="month" variant="input"
2910
+ label="To month" placeholder="Select end month"
2911
+ value={reportTo} onChange={setReportTo} onConfirm={setReportTo}
2912
+ formatDisplay={(d) => d.toLocaleDateString('en-US', { month: 'long', year: 'numeric' })}
2913
+ colors={{ selected: '#f59e0b', today: '#f59e0b', confirmBg: '#f59e0b' }}
2914
+ />
2915
+ ```
2916
+
2917
+ ---
2918
+
2919
+ ### StyledBottomSheet
2920
+
2921
+ Gesture-driven bottom sheet built on `PanResponder` with spring physics, configurable snap points, backdrop, scrollable content, and built-in header. Mounts inside a `Modal` — no portal required.
2922
+
2923
+ ```tsx
2924
+ import { StyledBottomSheet } from 'fluent-styles'
2925
+ ```
2926
+
2927
+ #### Variants
2928
+
2929
+ | Variant | Description |
2930
+ |---|---|
2931
+ | `default` | Standard bottom sheet sliding up from the bottom |
2932
+ | `modal` | Full-modal style with stronger overlay |
2933
+ | `sidebar` | Slides in from the side (landscape / tablet) |
2934
+
2935
+ #### Themes
2936
+
2937
+ | Theme | Surface |
2938
+ |---|---|
2939
+ | `light` | White background, gray handle (default) |
2940
+ | `dark` | `gray[900]` background, lighter handle and text |
2941
+
2942
+ #### `BottomSheetColors`
2943
+
2944
+ | Field | Default (light) | Description |
2945
+ |---|---|---|
2946
+ | `background` | `white` | Sheet surface colour |
2947
+ | `overlay` | `rgba(0,0,0,0.45)` | Backdrop tint |
2948
+ | `handle` | `gray[300]` | Drag handle pill |
2949
+ | `headerBorder` | `gray[100]` | Line under header |
2950
+ | `headerTitle` | `gray[900]` | Title text |
2951
+ | `headerSub` | `gray[400]` | Subtitle text |
2952
+ | `closeIconBg` | `gray[100]` | Close button background |
2953
+ | `closeIcon` | `gray[600]` | Close button icon colour |
2954
+
2955
+ #### Props
2956
+
2957
+ | Prop | Type | Default | Description |
2958
+ |---|---|---|---|
2959
+ | `visible` | `boolean` | required | Controls sheet visibility |
2960
+ | `onClose` | `() => void` | required | Called when sheet should close |
2961
+ | `snapPoints` | `(number \| string)[]` | `['40%','75%']` | Snap heights in px or `'50%'` percent strings |
2962
+ | `initialSnap` | `number` | `0` | Index of snap point to open at |
2963
+ | `title` | `ReactNode` | — | Header title |
2964
+ | `subtitle` | `ReactNode` | — | Header subtitle |
2965
+ | `showHandle` | `boolean` | `true` | Show drag handle pill |
2966
+ | `showClose` | `boolean` | `false` | Show ✕ close button |
2967
+ | `closeOnBackdrop` | `boolean` | `true` | Tap backdrop to close |
2968
+ | `destroyOnClose` | `boolean` | `false` | Unmount children when hidden |
2969
+ | `scrollable` | `boolean` | `false` | Wrap content in a `ScrollView` |
2970
+ | `variant` | `BottomSheetVariant` | `'default'` | Visual variant |
2971
+ | `sheetTheme` | `BottomSheetTheme` | `'light'` | Built-in colour theme |
2972
+ | `colors` | `Partial<BottomSheetColors>` | — | Per-token colour overrides |
2973
+ | `borderRadius` | `number` | `20` | Top corner radius |
2974
+ | `onOpen` | `() => void` | — | Fires when open animation starts |
2975
+ | `onOpened` | `() => void` | — | Fires when open animation ends |
2976
+ | `onClosed` | `() => void` | — | Fires when close animation ends |
2977
+
2978
+ #### Usage
2979
+
2980
+ ```tsx
2981
+ import React, { useState } from 'react'
2982
+ import { StyledBottomSheet, Stack, StyledText, StyledPressable } from 'fluent-styles'
2983
+
2984
+ const [visible, setVisible] = useState(false)
2985
+
2986
+ // ── 1. Plain sheet ────────────────────────────────────────────────────────────
2987
+ <StyledBottomSheet visible={visible} onClose={() => setVisible(false)}>
2988
+ <Stack padding={24} gap={8}>
2989
+ <StyledText fontSize={16} fontWeight="700">Plain sheet</StyledText>
2990
+ <StyledText fontSize={14} color="#6b7280">No header — just your content.</StyledText>
2991
+ </Stack>
2992
+ </StyledBottomSheet>
2993
+
2994
+ // ── 2. With title + subtitle ──────────────────────────────────────────────────
2995
+ <StyledBottomSheet
2996
+ visible={visible}
2997
+ onClose={() => setVisible(false)}
2998
+ title="Notifications"
2999
+ subtitle="Your recent alerts"
3000
+ showClose
3001
+ >
3002
+ <Stack padding={20} gap={12}>{/* content */}</Stack>
3003
+ </StyledBottomSheet>
3004
+
3005
+ // ── 3. Multiple snap points ───────────────────────────────────────────────────
3006
+ // Drag between 25 %, 50 %, and 85 % of screen height.
3007
+ <StyledBottomSheet
3008
+ visible={visible}
3009
+ onClose={() => setVisible(false)}
3010
+ title="Three snaps"
3011
+ showClose
3012
+ snapPoints={['25%', '50%', '85%']}
3013
+ >
3014
+ <Stack padding={20}>{/* content */}</Stack>
3015
+ </StyledBottomSheet>
3016
+
3017
+ // ── 4. Scrollable content ─────────────────────────────────────────────────────
3018
+ <StyledBottomSheet
3019
+ visible={visible}
3020
+ onClose={() => setVisible(false)}
3021
+ title="Long list"
3022
+ showClose
3023
+ scrollable
3024
+ snapPoints={['60%']}
3025
+ >
3026
+ <Stack padding={20} gap={10}>
3027
+ {Array.from({ length: 30 }).map((_, i) => (
3028
+ <StyledText key={i}>Item {i + 1}</StyledText>
3029
+ ))}
3030
+ </Stack>
3031
+ </StyledBottomSheet>
3032
+
3033
+ // ── 5. Dark theme ─────────────────────────────────────────────────────────────
3034
+ <StyledBottomSheet
3035
+ visible={visible}
3036
+ onClose={() => setVisible(false)}
3037
+ title="Dark sheet"
3038
+ subtitle="Midnight surface"
3039
+ showClose
3040
+ sheetTheme="dark"
3041
+ >
3042
+ <Stack padding={20} gap={12}>{/* content */}</Stack>
3043
+ </StyledBottomSheet>
3044
+
3045
+ // ── 6. Colour token overrides (indigo) ────────────────────────────────────────
3046
+ <StyledBottomSheet
3047
+ visible={visible}
3048
+ onClose={() => setVisible(false)}
3049
+ title="Indigo theme"
3050
+ showClose
3051
+ colors={{
3052
+ background: '#eef2ff',
3053
+ handle: '#a5b4fc',
3054
+ headerTitle: '#312e81',
3055
+ headerBorder: '#c7d2fe',
3056
+ closeIconBg: '#c7d2fe',
3057
+ closeIcon: '#4f46e5',
3058
+ }}
3059
+ >
3060
+ <Stack padding={20}>{/* content */}</Stack>
3061
+ </StyledBottomSheet>
3062
+
3063
+ // ── 7. Real-world: share sheet ────────────────────────────────────────────────
3064
+ <StyledBottomSheet
3065
+ visible={shareOpen}
3066
+ onClose={() => setShareOpen(false)}
3067
+ title="Share post"
3068
+ subtitle="Choose where to send"
3069
+ showClose
3070
+ snapPoints={['42%']}
3071
+ >
3072
+ <Stack padding={20} gap={16}>
3073
+ <Stack horizontal gap={20} justifyContent="center">
3074
+ {[{ icon: '💬', label: 'Messages' }, { icon: '✉️', label: 'Mail' },
3075
+ { icon: '🔗', label: 'Copy link' }, { icon: '📋', label: 'More' }]
3076
+ .map(({ icon, label }) => (
3077
+ <Stack key={label} alignItems="center" gap={6}>
3078
+ <StyledPressable width={52} height={52} borderRadius={26}
3079
+ backgroundColor="#f3f4f6" alignItems="center" justifyContent="center"
3080
+ onPress={() => setShareOpen(false)}>
3081
+ <StyledText fontSize={22}>{icon}</StyledText>
3082
+ </StyledPressable>
3083
+ <StyledText fontSize={11} color="#9ca3af">{label}</StyledText>
3084
+ </Stack>
3085
+ ))}
3086
+ </Stack>
3087
+ </Stack>
3088
+ </StyledBottomSheet>
3089
+
3090
+ // ── 8. Real-world: options menu ───────────────────────────────────────────────
3091
+ <StyledBottomSheet
3092
+ visible={optionsOpen}
3093
+ onClose={() => setOptionsOpen(false)}
3094
+ title="Post options"
3095
+ showClose
3096
+ snapPoints={['48%']}
3097
+ >
3098
+ <Stack paddingHorizontal={20} paddingBottom={16}>
3099
+ {[
3100
+ { icon: '✏️', label: 'Edit post', color: '#1f2937' },
3101
+ { icon: '🔗', label: 'Copy link', color: '#1f2937' },
3102
+ { icon: '🗑️', label: 'Delete post', color: '#e11d48' },
3103
+ ].map(({ icon, label, color }, idx, arr) => (
3104
+ <Stack key={label}>
3105
+ <StyledPressable flexDirection="row" gap={14} paddingVertical={14} alignItems="center"
3106
+ onPress={() => setOptionsOpen(false)}>
3107
+ <StyledText fontSize={20}>{icon}</StyledText>
3108
+ <StyledText fontSize={15} fontWeight="600" color={color}>{label}</StyledText>
3109
+ </StyledPressable>
3110
+ {idx < arr.length - 1 && <StyledDivider borderBottomColor="#f3f4f6" />}
3111
+ </Stack>
3112
+ ))}
3113
+ </Stack>
3114
+ </StyledBottomSheet>
3115
+
3116
+ // ── 9. Locked sheet (no backdrop dismiss) ─────────────────────────────────────
3117
+ <StyledBottomSheet
3118
+ visible={visible}
3119
+ onClose={() => setVisible(false)}
3120
+ title="Locked"
3121
+ showClose
3122
+ closeOnBackdrop={false}
3123
+ >
3124
+ <Stack padding={20}>
3125
+ <StyledText>Use the ✕ button to dismiss — backdrop tap is disabled.</StyledText>
3126
+ </Stack>
3127
+ </StyledBottomSheet>
3128
+ ```
3129
+
3130
+ ---
3131
+
3132
+ ### StyledEmptyState
3133
+
3134
+ Animated zero-state / empty-data display with 5 variants, an illustration slot (emoji, icon, or any `ReactNode`), primary + secondary action buttons, fade-in entrance animation, and a compact horizontal layout mode.
3135
+
3136
+ ```tsx
3137
+ import { StyledEmptyState } from 'fluent-styles'
3138
+ ```
3139
+
3140
+ #### Variants
3141
+
3142
+ | Variant | Description |
3143
+ |---|---|
3144
+ | `default` | Centred illustration + title + description + actions |
3145
+ | `card` | Same as default, rendered inside a subtle card surface |
3146
+ | `minimal` | Compact — small emoji, lighter text, single button |
3147
+ | `illustrated` | Large illustration circle, bolder visual weight |
3148
+ | `action` | Full-bleed coloured panel, designed for CTA prominence |
3149
+
3150
+ #### `EmptyStateColors`
3151
+
3152
+ | Field | Default | Description |
3153
+ |---|---|---|
3154
+ | `background` | `transparent` | Container background |
3155
+ | `illustrationBg` | `gray[100]` | Illustration circle fill |
3156
+ | `title` | `gray[900]` | Title text colour |
3157
+ | `description` | `gray[400]` | Description text colour |
3158
+ | `primaryBg` | `gray[900]` | Primary button background |
3159
+ | `primaryText` | `white` | Primary button text |
3160
+ | `primaryBorder` | `gray[900]` | Primary button border |
3161
+ | `secondaryBg` | `transparent` | Secondary button background |
3162
+ | `secondaryText` | `gray[700]` | Secondary button text |
3163
+ | `secondaryBorder` | `gray[200]` | Secondary button border |
3164
+ | `border` | `gray[100]` | Card variant border |
3165
+
3166
+ #### Props
3167
+
3168
+ | Prop | Type | Default | Description |
3169
+ |---|---|---|---|
3170
+ | `variant` | `EmptyStateVariant` | `'default'` | Visual style |
3171
+ | `illustration` | `ReactNode` | — | Emoji string, icon component, or any node |
3172
+ | `title` | `string` | — | Heading text |
3173
+ | `description` | `string` | — | Supporting body text |
3174
+ | `actions` | `EmptyStateAction[]` | `[]` | Array of action buttons |
3175
+ | `compact` | `boolean` | `false` | Horizontal layout for banners / list cells |
3176
+ | `animated` | `boolean` | `true` | Fade + slide-up entrance animation |
3177
+ | `colors` | `Partial<EmptyStateColors>` | — | Colour overrides |
3178
+ | `padding` | `number` | `32` | Padding around the container |
3179
+
3180
+ `EmptyStateAction` shape: `{ label: string; onPress: () => void; icon?: ReactNode; variant?: 'primary' | 'secondary' }`
3181
+
3182
+ #### Usage
3183
+
3184
+ ```tsx
3185
+ import { StyledEmptyState } from 'fluent-styles'
3186
+
3187
+ // ── 1. Default ────────────────────────────────────────────────────────────────
3188
+ <StyledEmptyState
3189
+ illustration="📭"
3190
+ title="No messages yet"
3191
+ description="When you receive a message, it'll show up here."
3192
+ actions={[{ label: 'Start a conversation', onPress: () => {} }]}
3193
+ />
3194
+
3195
+ // ── 2. Card variant with two actions ─────────────────────────────────────────
3196
+ <StyledEmptyState
3197
+ variant="card"
3198
+ illustration="🔍"
3199
+ title="No results found"
3200
+ description="Try adjusting your search or filters."
3201
+ actions={[
3202
+ { label: 'Clear filters', onPress: () => {}, variant: 'secondary' },
3203
+ { label: 'Try different terms', onPress: () => {}, variant: 'primary' },
3204
+ ]}
3205
+ />
3206
+
3207
+ // ── 3. Minimal ────────────────────────────────────────────────────────────────
3208
+ <StyledEmptyState
3209
+ variant="minimal"
3210
+ illustration="🌿"
3211
+ title="Nothing here yet"
3212
+ description="Add your first item to get started."
3213
+ actions={[{ label: '+ Add item', onPress: () => {} }]}
3214
+ />
3215
+
3216
+ // ── 4. Illustrated ────────────────────────────────────────────────────────────
3217
+ <StyledEmptyState
3218
+ variant="illustrated"
3219
+ illustration="🗂️"
3220
+ title="No files uploaded"
3221
+ description="Drop your documents here or tap the button below."
3222
+ actions={[
3223
+ { label: 'Upload file', onPress: () => {}, variant: 'primary' },
3224
+ { label: 'Browse Drive', onPress: () => {}, variant: 'secondary' },
3225
+ ]}
3226
+ />
3227
+
3228
+ // ── 5. Action-focused with colour override ────────────────────────────────────
3229
+ <StyledEmptyState
3230
+ variant="action"
3231
+ illustration="🚀"
3232
+ title="Ready to launch?"
3233
+ description="Set up your first project and deploy in seconds."
3234
+ actions={[{ label: 'Create project', onPress: () => {} }]}
3235
+ colors={{
3236
+ background: '#4f46e5',
3237
+ illustrationBg: 'rgba(255,255,255,0.15)',
3238
+ title: '#ffffff',
3239
+ description: 'rgba(255,255,255,0.75)',
3240
+ primaryBg: '#ffffff',
3241
+ primaryText: '#4338ca',
3242
+ primaryBorder: '#ffffff',
3243
+ }}
3244
+ />
3245
+
3246
+ // ── 6. Compact (horizontal banner) ───────────────────────────────────────────
3247
+ <StyledEmptyState
3248
+ compact
3249
+ illustration="📬"
3250
+ title="No notifications"
3251
+ description="You're all caught up."
3252
+ actions={[{ label: 'Mark all read', onPress: () => {} }]}
3253
+ />
3254
+
3255
+ // ── 7. No animation ───────────────────────────────────────────────────────────
3256
+ <StyledEmptyState
3257
+ illustration="📋"
3258
+ title="Empty"
3259
+ animated={false}
3260
+ />
3261
+ ```
3262
+
3263
+ ---
3264
+
3265
+ ### StyledSearchBar
3266
+
3267
+ Animated search input with focus ring, clear button, cancel, suggestion dropdown, and custom left icon / right action slots.
3268
+
3269
+ ```tsx
3270
+ import { StyledSearchBar } from 'fluent-styles'
3271
+ ```
3272
+
3273
+ #### Variants
3274
+
3275
+ | Variant | Description |
3276
+ |---|---|
3277
+ | `filled` | Solid grey background, no border (default) |
3278
+ | `outline` | Transparent background, visible border |
3279
+ | `ghost` | No background or border — blends into any surface |
3280
+ | `floating` | Drop-shadow, white background |
3281
+
3282
+ #### Sizes
3283
+
3284
+ | Size | Height | Font | Icon | Border radius |
3285
+ |---|---|---|---|---|
3286
+ | `sm` | 36 px | 13 px | 14 px | 10 px |
3287
+ | `md` | 44 px (default) | 15 px | 16 px | 12 px |
3288
+ | `lg` | 52 px | 17 px | 18 px | 14 px |
3289
+
3290
+ #### `SearchBarColors`
3291
+
3292
+ | Field | Default | Description |
3293
+ |---|---|---|
3294
+ | `background` | `gray[100]` | Input container fill |
3295
+ | `border` | `gray[200]` | Resting border |
3296
+ | `focusBorder` | `gray[900]` | Focused border |
3297
+ | `placeholder` | `gray[400]` | Placeholder text |
3298
+ | `text` | `gray[900]` | Input text |
3299
+ | `icon` | `gray[400]` | Search icon |
3300
+ | `clearBg` | `gray[300]` | Clear button background |
3301
+ | `clearIcon` | `gray[600]` | Clear button icon |
3302
+ | `cancelText` | `gray[900]` | Cancel button label |
3303
+ | `suggestionBg` | `white` | Suggestion dropdown background |
3304
+ | `suggestionText` | `gray[800]` | Suggestion item text |
3305
+ | `suggestionBorder` | `gray[100]` | Suggestion row divider |
3306
+ | `divider` | `gray[100]` | Divider between suggestions |
3307
+
3308
+ #### Props
3309
+
3310
+ | Prop | Type | Default | Description |
3311
+ |---|---|---|---|
3312
+ | `variant` | `SearchBarVariant` | `'filled'` | Visual style |
3313
+ | `size` | `SearchBarSize` | `'md'` | Height / font preset |
3314
+ | `placeholder` | `string` | `'Search…'` | Placeholder text |
3315
+ | `value` | `string` | — | Controlled value |
3316
+ | `onChangeText` | `(text: string) => void` | — | Text change handler |
3317
+ | `onSubmit` | `(text: string) => void` | — | Submit / search action |
3318
+ | `onCancel` | `() => void` | — | Cancel button handler |
3319
+ | `onClear` | `() => void` | — | Clear button handler |
3320
+ | `showCancel` | `boolean` | `false` | Show cancel button on focus |
3321
+ | `cancelLabel` | `string` | `'Cancel'` | Cancel button label |
3322
+ | `leftIcon` | `ReactNode` | search icon | Custom left icon |
3323
+ | `rightAction` | `ReactNode` | — | Right-side action slot (filter, voice, etc.) |
3324
+ | `suggestions` | `SearchSuggestion[]` | — | Dropdown suggestion items |
3325
+ | `onSuggestionPress` | `(item: SearchSuggestion) => void` | — | Suggestion tap handler |
3326
+ | `loading` | `boolean` | `false` | Replaces icon with spinner |
3327
+ | `disabled` | `boolean` | `false` | Disables input |
3328
+ | `autoFocus` | `boolean` | `false` | Focus on mount |
3329
+ | `colors` | `Partial<SearchBarColors>` | — | Colour overrides |
3330
+ | `borderRadius` | `number` | size preset | Border radius override |
3331
+
3332
+ `SearchSuggestion` shape: `{ id: string; label: string; subtitle?: string; icon?: ReactNode }`
3333
+
3334
+ Accepts all `TextInputProps` (except `style`).
3335
+
3336
+ #### Usage
3337
+
3338
+ ```tsx
3339
+ import React, { useState } from 'react'
3340
+ import { StyledSearchBar, type SearchSuggestion } from 'fluent-styles'
3341
+
3342
+ const [query, setQuery] = useState('')
3343
+
3344
+ // ── 1. Variants ───────────────────────────────────────────────────────────────
3345
+ <StyledSearchBar variant="filled" value={query} onChangeText={setQuery} />
3346
+ <StyledSearchBar variant="outline" value={query} onChangeText={setQuery} />
3347
+ <StyledSearchBar variant="ghost" value={query} onChangeText={setQuery} />
3348
+ <StyledSearchBar variant="floating" value={query} onChangeText={setQuery} />
3349
+
3350
+ // ── 2. Sizes ──────────────────────────────────────────────────────────────────
3351
+ <StyledSearchBar size="sm" placeholder="Small" />
3352
+ <StyledSearchBar size="md" placeholder="Medium (default)" />
3353
+ <StyledSearchBar size="lg" placeholder="Large" />
3354
+
3355
+ // ── 3. Cancel button ──────────────────────────────────────────────────────────
3356
+ <StyledSearchBar
3357
+ value={query}
3358
+ onChangeText={setQuery}
3359
+ showCancel
3360
+ onCancel={() => setQuery('')}
3361
+ />
3362
+
3363
+ // ── 4. Suggestion dropdown ────────────────────────────────────────────────────
3364
+ const SUGGESTIONS: SearchSuggestion[] = [
3365
+ { id: '1', label: 'React Native', subtitle: 'Framework' },
3366
+ { id: '2', label: 'TypeScript', subtitle: 'Language' },
3367
+ ]
3368
+
3369
+ <StyledSearchBar
3370
+ variant="outline"
3371
+ value={query}
3372
+ onChangeText={setQuery}
3373
+ suggestions={SUGGESTIONS.filter(s =>
3374
+ s.label.toLowerCase().includes(query.toLowerCase())
3375
+ )}
3376
+ onSuggestionPress={(item) => setQuery(item.label)}
3377
+ showCancel
3378
+ onCancel={() => setQuery('')}
3379
+ />
3380
+
3381
+ // ── 5. Right action slot (filter / voice) ─────────────────────────────────────
3382
+ <StyledSearchBar
3383
+ placeholder="Search with filter…"
3384
+ rightAction={
3385
+ <StyledPressable onPress={() => {}} paddingHorizontal={8} paddingVertical={4}
3386
+ borderRadius={8} backgroundColor="#f3f4f6">
3387
+ <StyledText fontSize={14}>⚙️</StyledText>
3388
+ </StyledPressable>
3389
+ }
3390
+ />
3391
+
3392
+ // ── 6. Custom left icon ───────────────────────────────────────────────────────
3393
+ <StyledSearchBar
3394
+ placeholder="Search locations…"
3395
+ leftIcon={<StyledText fontSize={16}>📍</StyledText>}
3396
+ />
3397
+
3398
+ // ── 7. Loading & disabled states ──────────────────────────────────────────────
3399
+ <StyledSearchBar variant="filled" value="react native" loading />
3400
+ <StyledSearchBar variant="outline" value="disabled" disabled />
3401
+
3402
+ // ── 8. Dark theme colour override ─────────────────────────────────────────────
3403
+ <StyledSearchBar
3404
+ variant="filled"
3405
+ value={query}
3406
+ onChangeText={setQuery}
3407
+ colors={{
3408
+ background: '#1f2937',
3409
+ border: '#374151',
3410
+ focusBorder: '#818cf8',
3411
+ placeholder: '#6b7280',
3412
+ text: '#f9fafb',
3413
+ icon: '#6b7280',
3414
+ clearBg: '#4b5563',
3415
+ clearIcon: '#e5e7eb',
3416
+ cancelText: '#f9fafb',
3417
+ suggestionBg: '#1f2937',
3418
+ suggestionText: '#f9fafb',
3419
+ suggestionBorder: '#374151',
3420
+ }}
3421
+ />
3422
+ ```
3423
+
3424
+ ---
3425
+
3426
+ ### StyledSkeleton
3427
+
3428
+ Animated loading placeholder with shimmer and pulse animations, 4 primitive shapes, 5 pre-built layout templates, and a `SkeletonBone` export for custom compositions.
3429
+
3430
+ ```tsx
3431
+ import { StyledSkeleton, SkeletonBone } from 'fluent-styles'
3432
+ ```
3433
+
3434
+ #### Templates
3435
+
3436
+ Pre-composed layouts that reflect common UI patterns. Pass `repeat` to stack multiple copies.
3437
+
3438
+ | Template | Layout |
3439
+ |---|---|
3440
+ | `card` | Banner image + title line + two text lines + avatar row |
3441
+ | `list-item` | Circle avatar + two text lines |
3442
+ | `profile` | Large avatar + name + subtitle + stats row |
3443
+ | `article` | Full-width image + three text lines |
3444
+ | `grid` | 2 × 2 card grid |
3445
+
3446
+ #### Shapes (primitives)
3447
+
3448
+ | Shape | Border radius |
3449
+ |---|---|
3450
+ | `rect` | `0` — sharp corners |
3451
+ | `rounded` | `8` px |
3452
+ | `text` | `4` px — matches typical text line height |
3453
+ | `circle` | `width / 2` — perfect circle |
3454
+
3455
+ #### Animation types
3456
+
3457
+ | Value | Behaviour |
3458
+ |---|---|
3459
+ | `shimmer` | Horizontal highlight sweep (default) |
3460
+ | `pulse` | Opacity fade in/out |
3461
+ | `none` | Static — no animation |
3462
+
3463
+ #### `SkeletonColors`
3464
+
3465
+ | Field | Default (light) | Description |
3466
+ |---|---|---|
3467
+ | `base` | `gray[100]` | Bone background colour |
3468
+ | `highlight` | `gray[50]` | Shimmer highlight colour |
3469
+ | `shimmer` | `rgba(255,255,255,0.65)` | Shimmer overlay |
3470
+
3471
+ #### Props
3472
+
3473
+ | Prop | Type | Default | Description |
3474
+ |---|---|---|---|
3475
+ | `width` | `number \| \`${number}%\`` | — | Bone width (primitive mode) |
3476
+ | `height` | `number` | — | Bone height (primitive mode) |
3477
+ | `shape` | `SkeletonShape` | `'rounded'` | Bone shape |
3478
+ | `borderRadius` | `number` | shape default | Custom border radius |
3479
+ | `template` | `SkeletonTemplate` | — | Render a pre-built layout |
3480
+ | `repeat` | `number` | `1` | Repeat template N times |
3481
+ | `animation` | `SkeletonAnimation` | `'shimmer'` | Animation type |
3482
+ | `speed` | `number` | `1400` | Cycle duration in ms — lower = faster |
3483
+ | `skeletonTheme` | `SkeletonTheme` | `'light'` | Built-in colour theme |
3484
+ | `colors` | `Partial<SkeletonColors>` | — | Per-token colour overrides |
3485
+
3486
+ #### Usage
3487
+
3488
+ ```tsx
3489
+ import { StyledSkeleton, SkeletonBone } from 'fluent-styles'
3490
+
3491
+ // ── 1. Templates ──────────────────────────────────────────────────────────────
3492
+ <StyledSkeleton template="card" animation="shimmer" />
3493
+ <StyledSkeleton template="list-item" animation="shimmer" repeat={3} />
3494
+ <StyledSkeleton template="profile" animation="shimmer" />
3495
+ <StyledSkeleton template="article" animation="shimmer" />
3496
+ <StyledSkeleton template="grid" animation="shimmer" />
3497
+
3498
+ // ── 2. Toggle with loaded content ─────────────────────────────────────────────
3499
+ const [loading, setLoading] = useState(true)
3500
+
3501
+ {loading
3502
+ ? <StyledSkeleton template="card" animation="shimmer" />
3503
+ : <MyContentCard />
3504
+ }
3505
+
3506
+ // ── 3. Primitive shapes ───────────────────────────────────────────────────────
3507
+ // Text lines
3508
+ <StyledSkeleton width="100%" height={13} shape="text" animation="shimmer" />
3509
+ <StyledSkeleton width="85%" height={13} shape="text" animation="shimmer" />
3510
+ <StyledSkeleton width="65%" height={13} shape="text" animation="shimmer" />
3511
+
3512
+ // Banner image
3513
+ <StyledSkeleton width="100%" height={160} shape="rounded" animation="shimmer" />
3514
+
3515
+ // Avatars
3516
+ <StyledSkeleton width={48} height={48} shape="circle" animation="shimmer" />
3517
+ <StyledSkeleton width={64} height={64} shape="circle" animation="shimmer" />
3518
+
3519
+ // ── 4. Animation types ────────────────────────────────────────────────────────
3520
+ <StyledSkeleton width="100%" height={14} animation="shimmer" />
3521
+ <StyledSkeleton width="100%" height={14} animation="pulse" />
3522
+ <StyledSkeleton width="100%" height={14} animation="none" />
3523
+
3524
+ // ── 5. Speed control ──────────────────────────────────────────────────────────
3525
+ <StyledSkeleton width="100%" height={14} animation="shimmer" speed={600} /> {/* fast */}
3526
+ <StyledSkeleton width="100%" height={14} animation="shimmer" speed={1400} /> {/* default */}
3527
+ <StyledSkeleton width="100%" height={14} animation="shimmer" speed={2200} /> {/* slow */}
3528
+
3529
+ // ── 6. Dark theme ─────────────────────────────────────────────────────────────
3530
+ <StyledSkeleton template="profile" animation="shimmer" skeletonTheme="dark" />
3531
+
3532
+ // ── 7. Colour overrides (indigo) ──────────────────────────────────────────────
3533
+ <StyledSkeleton width="100%" height={13} shape="text" animation="pulse"
3534
+ colors={{ base: '#e0e7ff', highlight: '#eef2ff', shimmer: 'rgba(99,102,241,0.15)' }} />
3535
+
3536
+ // ── 8. Custom layout with SkeletonBone ────────────────────────────────────────
3537
+ // SkeletonBone renders a single animated bone directly — useful for bespoke layouts.
3538
+ <Stack horizontal gap={14} alignItems="center" padding={16}>
3539
+ <SkeletonBone width={48} height={48} shape="circle" animation="shimmer" speed={1400}
3540
+ colors={{ base: '#fde68a', highlight: '#fef9c3', shimmer: 'rgba(245,158,11,0.25)' }} />
3541
+ <Stack flex={1} gap={8}>
3542
+ <SkeletonBone width="70%" height={14} shape="text" animation="shimmer" speed={1400}
3543
+ colors={{ base: '#fde68a', highlight: '#fef9c3', shimmer: 'rgba(245,158,11,0.25)' }} />
3544
+ <SkeletonBone width="45%" height={12} shape="text" animation="shimmer" speed={1400}
3545
+ colors={{ base: '#fde68a', highlight: '#fef9c3', shimmer: 'rgba(245,158,11,0.25)' }} />
3546
+ </Stack>
3547
+ </Stack>
3548
+ ```
3549
+
3550
+ ---
3551
+
3552
+ ## Hooks
3553
+
3554
+ All hooks require a `PortalManager` ancestor.
3555
+
3556
+ ### useToast
3557
+
3558
+ ```tsx
3559
+ import { useToast } from 'fluent-styles'
3560
+
3561
+ const toast = useToast()
3562
+
3563
+ // --- Shortcut methods ---
3564
+ toast.success('Profile saved')
3565
+ toast.error('Upload failed', 'The selected file is larger than 5 MB.')
3566
+ toast.warning('Unsaved changes', 'You have pending edits on this screen.')
3567
+ toast.info('New update available', 'Restart the app to use the latest version.')
3568
+
3569
+ // --- Full control with show() ---
3570
+ const id = toast.show({
3571
+ message: 'Settings updated',
3572
+ description: 'Your preferences were saved successfully.',
3573
+ variant: 'success', // 'success' | 'error' | 'warning' | 'info'
3574
+ duration: 2500,
3575
+ theme: 'light', // 'light' | 'dark' | 'system'
3576
+ })
3577
+
3578
+ // Dark-themed toast
3579
+ toast.show({
3580
+ message: 'Background sync started',
3581
+ description: 'We will notify you when sync is complete.',
3582
+ variant: 'info',
3583
+ duration: 4000,
3584
+ theme: 'dark',
3585
+ })
3586
+
3587
+ // --- Persistent toast (duration: 0 — never auto-dismisses) ---
3588
+ const persistId = toast.show({
3589
+ message: 'Uploading file…',
3590
+ description: 'Please keep the app open until upload finishes.',
3591
+ variant: 'info',
3592
+ duration: 0,
3593
+ theme: 'dark',
3594
+ })
3595
+ toast.dismiss(persistId) // dismiss manually later
3596
+
3597
+ // --- Short / long durations ---
3598
+ toast.show({ message: 'Quick message', variant: 'info', duration: 1200, theme: 'light' })
3599
+ toast.show({ message: 'Read this carefully', variant: 'warning', duration: 6000, theme: 'light' })
3600
+
3601
+ // --- Color token overrides ---
3602
+ toast.show({
3603
+ message: 'Custom success',
3604
+ variant: 'success',
3605
+ theme: 'light',
3606
+ colors: {
3607
+ successBg: '#ecfdf5',
3608
+ successBorder: '#10b981',
3609
+ successLabel: '#065f46',
3610
+ description: '#047857',
3611
+ closeIcon: '#065f46',
3612
+ },
3613
+ })
3614
+
3615
+ toast.show({
3616
+ message: 'Custom error',
3617
+ variant: 'error',
3618
+ theme: 'dark',
3619
+ colors: {
3620
+ errorBg: '#3b0a0a',
3621
+ errorBorder: '#ef4444',
3622
+ errorLabel: '#fecaca',
3623
+ description: '#fca5a5',
3624
+ closeIcon: '#fecaca',
3625
+ },
3626
+ })
3627
+
3628
+ // --- Dismiss ---
3629
+ toast.dismiss(id) // single
3630
+ toast.dismissAll() // all active
3631
+ ```
3632
+
3633
+ | Method | Signature | Description |
3634
+ |---|---|---|
3635
+ | `show` | `(options) => number` | Show a toast, returns portal id |
3636
+ | `success` | `(message, description?) => number` | Green success toast |
3637
+ | `error` | `(message, description?) => number` | Red error toast |
3638
+ | `warning` | `(message, description?) => number` | Amber warning toast |
3639
+ | `info` | `(message, description?) => number` | Blue info toast |
3640
+ | `dismiss` | `(id: number) => void` | Dismiss specific toast |
3641
+ | `dismissAll` | `() => void` | Dismiss all active toasts |
3642
+
3643
+ **`show` options:** `message`, `description?`, `variant`, `duration` (`0` = persistent), `theme`, `colors`
3644
+
3645
+ ---
3646
+
3647
+ ### useNotification
3648
+
3649
+ ```tsx
3650
+ import { useNotification } from 'fluent-styles'
3651
+
3652
+ const notification = useNotification()
3653
+
3654
+ // --- Basic notification ---
3655
+ const id = notification.show({
3656
+ title: 'New message from Alex',
3657
+ body: 'Hey, are you free this afternoon?',
3658
+ source: 'Messages',
3659
+ initials: 'AK',
3660
+ timestamp: 'now',
3661
+ theme: 'dark',
3662
+ })
3663
+
3664
+ // --- With avatar image ---
3665
+ notification.show({
3666
+ title: 'Sarah Johnson',
3667
+ body: 'Sent you 3 new design files.',
3668
+ source: 'Drive',
3669
+ avatar: { uri: 'https://example.com/avatar.jpg' },
3670
+ timestamp: '2m',
3671
+ theme: 'light',
3672
+ })
3673
+
3674
+ // --- With action button ---
3675
+ notification.show({
3676
+ title: 'Deployment finished',
3677
+ body: 'Production build completed successfully.',
3678
+ source: 'CI/CD',
3679
+ initials: 'CI',
3680
+ timestamp: 'now',
3681
+ actionLabel: 'Open',
3682
+ onAction: () => navigate('Dashboard'),
3683
+ theme: 'dark',
3684
+ })
3685
+
3686
+ // --- Custom duration ---
3687
+ notification.show({ title: 'Quick', body: 'Disappears fast', initials: 'Q', duration: 1500, theme: 'light' })
3688
+ notification.show({ title: 'Long', body: 'Stays a while', initials: 'L', duration: 8000, theme: 'dark' })
3689
+
3690
+ // --- Color token overrides ---
3691
+ notification.show({
3692
+ title: 'Custom brand notification',
3693
+ body: 'Using token overrides on top of the active theme.',
3694
+ source: 'Brand',
3695
+ initials: 'BR',
3696
+ timestamp: 'now',
3697
+ theme: 'light',
3698
+ actionLabel: 'View',
3699
+ onAction: () => navigate('Brand'),
3700
+ colors: {
3701
+ background: '#eff6ff',
3702
+ border: '#2563eb',
3703
+ title: '#1e3a8a',
3704
+ body: '#1d4ed8',
3705
+ source: '#2563eb',
3706
+ timestamp: '#3b82f6',
3707
+ avatarBg: '#dbeafe',
3708
+ avatarBorder: '#60a5fa',
3709
+ avatarInitials: '#1d4ed8',
3710
+ actionBg: '#dbeafe',
3711
+ actionLabel: '#1d4ed8',
3712
+ closeIcon: '#1d4ed8',
3713
+ },
3714
+ })
3715
+
3716
+ // --- Real-world examples ---
3717
+ notification.show({
3718
+ title: 'New comment on your PR',
3719
+ body: 'Chris left feedback on the latest changes.',
3720
+ source: 'Git',
3721
+ initials: 'CK',
3722
+ timestamp: '1m',
3723
+ actionLabel: 'Review',
3724
+ onAction: () => navigate('PRReview'),
3725
+ theme: 'dark',
3726
+ })
3727
+
3728
+ notification.show({
3729
+ title: 'Meeting starts in 10 minutes',
3730
+ body: 'Frontend sync with the product team.',
3731
+ source: 'Calendar',
3732
+ initials: 'CA',
3733
+ timestamp: 'soon',
3734
+ actionLabel: 'Join',
3735
+ onAction: joinMeeting,
3736
+ theme: 'light',
3737
+ })
3738
+
3739
+ notification.dismiss(id)
3740
+ ```
3741
+
3742
+ **Show options:** `title`, `body`, `avatar`, `initials`, `source`, `timestamp`, `actionLabel`, `onAction`, `duration` (`0` = persistent), `theme`, `colors`
3743
+
3744
+ ---
3745
+
3746
+ ### useDialogue
3747
+
3748
+ ```tsx
3749
+ import { useDialogue } from 'fluent-styles'
3750
+
3751
+ const dialogue = useDialogue()
3752
+
3753
+ // --- Alert (Promise<void>) ---
3754
+ await dialogue.alert(
3755
+ 'Session expired',
3756
+ 'Please log in again to continue.',
3757
+ '🔒',
3758
+ 'light', // optional theme
3759
+ )
3760
+
3761
+ // --- Confirm (Promise<boolean>) ---
3762
+ const confirmed = await dialogue.confirm({
3763
+ title: 'Save changes?',
3764
+ message: 'Your edits will be saved to this project.',
3765
+ icon: '💾',
3766
+ confirmLabel: 'Save',
3767
+ cancelLabel: 'Cancel',
3768
+ theme: 'light',
3769
+ })
3770
+ if (confirmed) save()
3771
+
3772
+ // --- Destructive confirm ---
3773
+ const ok = await dialogue.confirm({
3774
+ title: 'Delete project?',
3775
+ message: 'This action cannot be undone.',
3776
+ icon: '⚠️',
3777
+ confirmLabel: 'Delete',
3778
+ cancelLabel: 'Keep it',
3779
+ destructive: true,
3780
+ })
3781
+ if (ok) deleteProject()
3782
+
3783
+ // --- Custom multi-action dialogue ---
3784
+ dialogue.show({
3785
+ title: 'Unsaved changes',
3786
+ message: 'You have unsaved edits. What would you like to do?',
3787
+ icon: '📝',
3788
+ theme: 'light',
3789
+ actions: [
3790
+ { label: 'Discard', variant: 'destructive', onPress: () => discard() },
3791
+ { label: 'Save draft', variant: 'secondary', onPress: () => saveDraft() },
3792
+ { label: 'Keep editing', variant: 'primary', onPress: () => keepEditing() },
3793
+ ],
3794
+ })
3795
+
3796
+ // --- Async chained flow (confirm then alert) ---
3797
+ const publish = async () => {
3798
+ const confirmed = await dialogue.confirm({
3799
+ title: 'Publish update?',
3800
+ message: 'This will make the latest version visible to users.',
3801
+ icon: '🚀',
3802
+ confirmLabel: 'Publish',
3803
+ cancelLabel: 'Not now',
3804
+ theme: 'light',
3805
+ })
3806
+ if (!confirmed) return
3807
+
3808
+ await performPublish()
3809
+
3810
+ await dialogue.alert('Published', 'Your update is now live.', '✅')
3811
+ }
3812
+
3813
+ // --- Programmatic dismiss by id ---
3814
+ const id = dialogue.show({
3815
+ title: 'Temporary dialogue',
3816
+ message: 'This will close automatically in 2 seconds.',
3817
+ icon: '⏳',
3818
+ theme: 'light',
3819
+ actions: [{ label: 'OK', variant: 'primary', onPress: () => {} }],
3820
+ })
3821
+ setTimeout(() => dialogue.dismiss(id), 2000)
3822
+
3823
+ // --- Real-world: log out + rate app ---
3824
+ const handleLogout = async () => {
3825
+ const ok = await dialogue.confirm({ title: 'Log out?', message: 'You will need to sign in again.', icon: '👋', confirmLabel: 'Log out', destructive: true })
3826
+ if (ok) logout()
3827
+ }
3828
+
3829
+ const handleRateApp = () => {
3830
+ dialogue.show({
3831
+ title: 'Enjoying the app?',
3832
+ icon: '⭐',
3833
+ actions: [
3834
+ { label: '😠 1', variant: 'secondary', onPress: () => submitRating(1) },
3835
+ { label: '😐 3', variant: 'secondary', onPress: () => submitRating(3) },
3836
+ { label: '😁 5', variant: 'primary', onPress: () => submitRating(5) },
3837
+ ],
3838
+ })
3839
+ }
3840
+ ```
3841
+
3842
+ **Action variants:** `primary` | `secondary` | `destructive`
3843
+
3844
+ ---
3845
+
3846
+ ### useActionSheet
3847
+
3848
+ ```tsx
3849
+ import { useActionSheet } from 'fluent-styles'
3850
+
3851
+ const actionSheet = useActionSheet()
3852
+
3853
+ // Items list
3854
+ actionSheet.show({
3855
+ title: 'Post options',
3856
+ items: [
3857
+ { icon: '✏️', label: 'Edit', onPress: onEdit },
3858
+ { icon: '🔗', label: 'Copy link', onPress: onCopy },
3859
+ { icon: '🚩', label: 'Report', variant: 'destructive', onPress: onReport },
3860
+ { icon: '🔒', label: 'Premium', variant: 'disabled' },
3861
+ ],
3862
+ })
3863
+
3864
+ // Custom content sheet
3865
+ actionSheet.present(<MyDatePicker onChange={setDate} />, { title: 'Pick a date' })
3866
+
3867
+ // Mixed: content + items
3868
+ actionSheet.show({
3869
+ title: 'Choose a colour',
3870
+ children: <ColorSwatchRow onSelect={setColor} />,
3871
+ items: [{ label: 'Reset to default', onPress: resetColor }],
3872
+ })
3873
+ ```
3874
+
3875
+ **ActionSheetItem variants:** `default` | `destructive` | `disabled`
3876
+
3877
+ ---
3878
+
3879
+ ### useLoader
3880
+
3881
+ ```tsx
3882
+ import { useLoader } from 'fluent-styles'
3883
+
3884
+ const loader = useLoader()
3885
+
3886
+ // --- Manual show / hide ---
3887
+ const id = loader.show({ label: 'Saving…', variant: 'spinner' })
3888
+ await saveData()
3889
+ loader.hide(id)
3890
+
3891
+ // --- Variants ---
3892
+ loader.show({ variant: 'spinner' })
3893
+ loader.show({ variant: 'dots', label: 'Processing…' })
3894
+ loader.show({ variant: 'pulse', overlay: true })
3895
+ loader.show({ variant: 'circular', label: 'Loading…', theme: 'dark' })
3896
+
3897
+ // --- Color overrides ---
3898
+ loader.show({
3899
+ label: 'Preparing analytics…',
3900
+ variant: 'circular',
3901
+ theme: 'dark',
3902
+ colors: { indicator: '#60a5fa', label: '#dbeafe' },
3903
+ })
3904
+
3905
+ // --- Automatic wrap (always hides, even on error) ---
3906
+ const report = await loader.wrap(
3907
+ () => api.fetchReport(),
3908
+ { label: 'Loading report…', variant: 'dots' },
3909
+ )
3910
+
3911
+ // --- Wrap example with status feedback ---
3912
+ const runFakeTask = async (options, successMsg) => {
3913
+ const result = await loader.wrap(
3914
+ () => new Promise(resolve => setTimeout(resolve, 2000)),
3915
+ options,
3916
+ )
3917
+ toast.success(successMsg)
3918
+ }
3919
+ await runFakeTask({ label: 'Saving profile…', variant: 'spinner' }, 'Profile saved')
3920
+ await runFakeTask({ label: 'Uploading data…', variant: 'circular' }, 'Upload complete')
3921
+ ```
3922
+
3923
+ ---
3924
+
3925
+ ## Imperative Services
3926
+
3927
+ These services are callable from **anywhere** — Redux middleware, Axios interceptors, navigation helpers — because they use the global `portal` singleton. No `PortalManager` is required.
3928
+
3929
+ ### toastService
3930
+
3931
+ ```ts
3932
+ import { toastService } from 'fluent-styles'
3933
+
3934
+ toastService.success('Saved!')
3935
+ toastService.error('Network error', 'Check your connection.')
3936
+ toastService.warning('Session expiring')
3937
+ toastService.info('New version available')
3938
+
3939
+ const id = toastService.show({ message: 'Custom', variant: 'info', duration: 2000 })
3940
+ toastService.dismiss(id)
3941
+ ```
3942
+
3943
+ ### notificationService
3944
+
3945
+ ```ts
3946
+ import { notificationService } from 'fluent-styles'
3947
+
3948
+ const id = notificationService.show({
3949
+ title: 'Payment received',
3950
+ body: '$49.99 from John Smith',
3951
+ initials: 'JS',
3952
+ })
3953
+ notificationService.dismiss(id)
3954
+ ```
3955
+
3956
+ ### dialogueService
3957
+
3958
+ ```ts
3959
+ import { dialogueService } from 'fluent-styles'
3960
+
3961
+ const ok = await dialogueService.confirm({ title: 'Sign out?', destructive: true })
3962
+ await dialogueService.alert('Welcome back!', 'You were away for 3 days.')
3963
+
3964
+ dialogueService.show({ title: 'Custom', actions: [{ label: 'Got it', onPress: () => {} }] })
3965
+ ```
3966
+
3967
+ ### actionSheetService
3968
+
3969
+ ```ts
3970
+ import { actionSheetService } from 'fluent-styles'
3971
+
3972
+ actionSheetService.show({
3973
+ title: 'Share',
3974
+ items: [
3975
+ { icon: '📋', label: 'Copy link', onPress: copyLink },
3976
+ { icon: '✉️', label: 'Email', onPress: shareEmail },
3977
+ ],
3978
+ })
3979
+
3980
+ actionSheetService.present(<MyPicker />, { title: 'Choose' })
3981
+ ```
3982
+
3983
+ ### loaderService
3984
+
3985
+ ```ts
3986
+ import { loaderService } from 'fluent-styles'
3987
+
3988
+ const id = loaderService.show({ label: 'Uploading…', variant: 'circular' })
3989
+ await uploadFile()
3990
+ loaderService.hide(id)
3991
+
3992
+ // Wrapped — always hides, even on error
3993
+ const result = await loaderService.wrap(() => api.submit(form), { label: 'Submitting…' })
3994
+ ```
3995
+
3996
+ ---
3997
+
3998
+ ## Theme & Tokens
3999
+
4000
+ The design token system is fully exported for use in your own components.
4001
+
4002
+ ```ts
4003
+ import { theme, palettes, lightColors, darkColors, fontStyles } from 'fluent-styles'
4004
+
4005
+ // Colour scales (50–900)
4006
+ theme.colors.indigo[500] // '#6366f1'
4007
+ theme.colors.rose[600] // '#e11d48'
4008
+ theme.colors.gray[100] // '#f4f4f5'
4009
+
4010
+ // Base values
4011
+ palettes.white // '#FFFFFF'
4012
+ palettes.black // '#000000'
4013
+
4014
+ // Typography scale
4015
+ theme.fontSize.small
4016
+ theme.fontSize.normal
4017
+ theme.fontSize.medium
4018
+ theme.fontSize.large
4019
+
4020
+ theme.fontWeight.normal
4021
+ theme.fontWeight.semiBold
4022
+ theme.fontWeight.bold
4023
+
4024
+ // Prebuilt font style objects
4025
+ fontStyles.body
4026
+ fontStyles.heading
4027
+ ```
4028
+
4029
+ ### Per-component colour token overrides
4030
+
4031
+ Every complex component accepts a `colors` prop typed as `Partial<ComponentColors>`. Exported default token maps:
4032
+
4033
+ | Export | Used by |
4034
+ |---|---|
4035
+ | `TAB_BAR_COLORS_LIGHT` / `TAB_BAR_COLORS_DARK` | `TabBar` |
4036
+ | `POPUP_COLORS_LIGHT` / `POPUP_COLORS_DARK` | `Popup` |
4037
+ | `DRAWER_COLORS_LIGHT` / `DRAWER_COLORS_DARK` | `Drawer` |
4038
+ | `COLLAPSE_LIGHT` / `COLLAPSE_DARK` | `Collapse` |
4039
+ | `LOADER_LIGHT` / `LOADER_DARK` | `Loader` |
4040
+
4041
+ ### `theme` prop
4042
+
4043
+ Overlay and feedback components accept a `theme` prop:
4044
+
4045
+ ```tsx
4046
+ <Popup theme="dark" visible={…}>…</Popup>
4047
+ <Loader theme="system" /> {/* follows device dark mode */}
4048
+ ```
4049
+
4050
+ Values: `'light'` | `'dark'` | `'system'`
4051
+
4052
+ ---
4053
+
4054
+ ## Contributing
4055
+
4056
+ Issues and pull requests are welcome. Please open an issue first to discuss any significant changes.
4057
+
4058
+ - Repository: [github.com/aaghorighor/fluent-styles](https://github.com/aaghorighor/fluent-styles)
4059
+ - Bug reports: [github.com/aaghorighor/fluent-styles/issues](https://github.com/aaghorighor/fluent-styles/issues)
4060
+
4061
+ ---
4062
+
4063
+ ## License
4064
+
4065
+ [Apache 2.0](LICENSE)