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