diff --git a/03_source/mobile/src/pages/DemoReactSwitchTabs/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoReactSwitchTabs/AppPages/Tab1.tsx similarity index 96% rename from 03_source/mobile/src/pages/DemoReactSwitchTabs/AppPages/Tab1.jsx rename to 03_source/mobile/src/pages/DemoReactSwitchTabs/AppPages/Tab1.tsx index 24873ba..d14c4a9 100644 --- a/03_source/mobile/src/pages/DemoReactSwitchTabs/AppPages/Tab1.jsx +++ b/03_source/mobile/src/pages/DemoReactSwitchTabs/AppPages/Tab1.tsx @@ -17,7 +17,7 @@ import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; import './Tab1.css'; -const Tab1 = () => { +const Tab1 = (): React.JSX.Element => { const router = useIonRouter(); function handleBackClick() { diff --git a/03_source/mobile/src/pages/DemoReactSwitchTabs/AppPages/Tab2.tsx b/03_source/mobile/src/pages/DemoReactSwitchTabs/AppPages/Tab2.tsx new file mode 100644 index 0000000..f69a263 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactSwitchTabs/AppPages/Tab2.tsx @@ -0,0 +1,25 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab2.css'; + +const Tab2 = (): React.JSX.Element => { + return ( + + + + Profile + + + + + + Profile + + + + + + ); +}; + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoReactSwitchTabs/AppPages/Tab3.tsx b/03_source/mobile/src/pages/DemoReactSwitchTabs/AppPages/Tab3.tsx new file mode 100644 index 0000000..48d6189 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactSwitchTabs/AppPages/Tab3.tsx @@ -0,0 +1,25 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab3.css'; + +const Tab3 = (): React.JSX.Element => { + return ( + + + + Settings + + + + + + Settings + + + + + + ); +}; + +export default Tab3; diff --git a/03_source/mobile/src/pages/DemoReactSwitchTabs/TestComponents/ExploreContainer.css b/03_source/mobile/src/pages/DemoReactSwitchTabs/TestComponents/ExploreContainer.css new file mode 100644 index 0000000..e99f514 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactSwitchTabs/TestComponents/ExploreContainer.css @@ -0,0 +1,24 @@ +.container { + text-align: center; + position: absolute; + left: 0; + right: 0; + top: 50%; + transform: translateY(-50%); +} + +.container strong { + font-size: 20px; + line-height: 26px; +} + +.container p { + font-size: 16px; + line-height: 22px; + color: #8c8c8c; + margin: 0; +} + +.container a { + text-decoration: none; +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoReactSwitchTabs/TestComponents/ExploreContainer.tsx b/03_source/mobile/src/pages/DemoReactSwitchTabs/TestComponents/ExploreContainer.tsx new file mode 100644 index 0000000..202447c --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactSwitchTabs/TestComponents/ExploreContainer.tsx @@ -0,0 +1,15 @@ +import './ExploreContainer.css'; + +interface ExploreContainerProps { + name: string; +} + +const ExploreContainer = ({ name }: ExploreContainerProps): React.JSX.Element => { + return ( +
+ {name} +
+ ); +}; + +export default ExploreContainer; diff --git a/03_source/mobile/src/pages/DemoReactSwitchTabs/components/SwitchTabBar.jsx b/03_source/mobile/src/pages/DemoReactSwitchTabs/TestComponents/SwitchTabBar.tsx similarity index 86% rename from 03_source/mobile/src/pages/DemoReactSwitchTabs/components/SwitchTabBar.jsx rename to 03_source/mobile/src/pages/DemoReactSwitchTabs/TestComponents/SwitchTabBar.tsx index 6ec16cf..08fdc46 100644 --- a/03_source/mobile/src/pages/DemoReactSwitchTabs/components/SwitchTabBar.jsx +++ b/03_source/mobile/src/pages/DemoReactSwitchTabs/TestComponents/SwitchTabBar.tsx @@ -16,9 +16,24 @@ import { useRef } from 'react'; import { useEffect, useState } from 'react'; import { Redirect, Route } from 'react-router'; -const SwitchTabBar = () => { +interface TabItem { + label: string; + url: string; + icon: string; + color: string; + backgroundColor: string; + component: React.ComponentType; +} + +interface AnimationConfig { + property: string; + fromValue: string; + toValue: string; +} + +const SwitchTabBar = (): React.JSX.Element => { const [activeTab, setActiveTab] = useState('tab0'); - const switchRefs = useRef([]); + const switchRefs = useRef<(CreateAnimation | null)[]>([]); const tabs = [ { @@ -61,8 +76,8 @@ const SwitchTabBar = () => { easing: 'ease-in-out', }; - const getTabButtonStyle = (tab) => { - const tabStyle = { + const getTabButtonStyle = (tab: TabItem): React.CSSProperties => { + const tabStyle: React.CSSProperties = { backgroundColor: tab.backgroundColor, color: tab.color, transition: '0.5s all ease-in-out', diff --git a/03_source/mobile/src/pages/DemoReactSwitchTabs/components/ExploreContainer.jsx b/03_source/mobile/src/pages/DemoReactSwitchTabs/components/ExploreContainer.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactSwitchTabs/components/ExploreContainer.jsx rename to 03_source/mobile/src/pages/DemoReactSwitchTabs/components/ExploreContainer.tsx diff --git a/03_source/mobile/src/pages/DemoReactSwitchTabs/components/SwitchTabBar.tsx b/03_source/mobile/src/pages/DemoReactSwitchTabs/components/SwitchTabBar.tsx new file mode 100644 index 0000000..7968810 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactSwitchTabs/components/SwitchTabBar.tsx @@ -0,0 +1,121 @@ +import { CreateAnimation, IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from "@ionic/react"; +import { IonReactRouter } from "@ionic/react-router"; +import { home, person, settings } from 'ionicons/icons'; +import Tab1 from '../pages/Tab1'; +import Tab2 from '../pages/Tab2'; +import Tab3 from '../pages/Tab3'; +import { useRef } from "react"; +import { useEffect, useState } from "react"; +import { Redirect, Route } from "react-router"; + +const SwitchTabBar = () => { + + const [activeTab, setActiveTab] = useState("tab0"); + const switchRefs = useRef([]); + + const tabs = [ + + { + label: "Home", + url: "/home", + icon: home, + color: "#76b140", + backgroundColor: "#ddf7c5", + component: Tab1 + }, + { + label: "Profile", + url: "/profile", + icon: person, + color: "#e46062", + backgroundColor: "#fcddde", + component: Tab2 + }, + { + label: "Settings", + url: "/settings", + icon: settings, + color: "#3578e5", + backgroundColor: "#e7f0ff", + component: Tab3 + } + ]; + + const revealAnimation = { + + property: "transform", + fromValue: "translateX(-30px)", + toValue: "translateX(0px)" + }; + + const switchAnimation = { + + duration: 200, + direction: "normal", + iterations: "1", + fromTo: [revealAnimation], + easing: "ease-in-out" + }; + + const getTabButtonStyle = tab => { + + const tabStyle = { + + backgroundColor: tab.backgroundColor, + color: tab.color, + transition: "0.5s all ease-in-out" + }; + + return tabStyle; + } + + useEffect(() => { + + const tabIndex = activeTab.match(/\d+/)[0]; + switchRefs.current[tabIndex].animation.play(); + }, [activeTab]); + + return ( + + + + + + {tabs.map((tab, index) => { + + return ( + + + + ); + })} + + + + + + setActiveTab(e.detail.tab)}> + + {tabs.map((tab, index) => { + + const tabStyle = getTabButtonStyle(tab); + const isActive = activeTab === `tab${index}`; + + return ( + + + + + {isActive && switchRefs.current[index] = ref} {...switchAnimation}> + {tab.label} + } + + ); + })} + + + + ); +} + +export default SwitchTabBar; \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoReactSwitchTabs/pages/Tab1.css b/03_source/mobile/src/pages/DemoReactSwitchTabs/pages/Tab1.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile/src/pages/DemoReactSwitchTabs/pages/Tab1.tsx b/03_source/mobile/src/pages/DemoReactSwitchTabs/pages/Tab1.tsx new file mode 100644 index 0000000..20895c3 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactSwitchTabs/pages/Tab1.tsx @@ -0,0 +1,25 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab1.css'; + +const Tab1 = () => { + return ( + + + + Home + + + + + + Home + + + + + + ); +}; + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoReactSwitchTabs/pages/Tab2.css b/03_source/mobile/src/pages/DemoReactSwitchTabs/pages/Tab2.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile/src/pages/DemoReactSwitchTabs/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoReactSwitchTabs/pages/Tab2.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactSwitchTabs/AppPages/Tab2.jsx rename to 03_source/mobile/src/pages/DemoReactSwitchTabs/pages/Tab2.tsx diff --git a/03_source/mobile/src/pages/DemoReactSwitchTabs/pages/Tab3.css b/03_source/mobile/src/pages/DemoReactSwitchTabs/pages/Tab3.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile/src/pages/DemoReactSwitchTabs/AppPages/Tab3.jsx b/03_source/mobile/src/pages/DemoReactSwitchTabs/pages/Tab3.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactSwitchTabs/AppPages/Tab3.jsx rename to 03_source/mobile/src/pages/DemoReactSwitchTabs/pages/Tab3.tsx diff --git a/03_source/mobile/src/pages/DemoReactSwitchTabs/theme/variables.css b/03_source/mobile/src/pages/DemoReactSwitchTabs/theme/variables.css new file mode 100644 index 0000000..6e146ff --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactSwitchTabs/theme/variables.css @@ -0,0 +1,99 @@ +/* Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ */ + +/** Ionic CSS Variables **/ +:root { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; +} + +ion-tab-bar { + + padding: 1rem; +} + +ion-tab-button { + + flex-direction: row; + border-radius: 20px; +} + +ion-tab-button ion-icon { + + font-size: 1.5rem; +} + +ion-tab-button ion-label { + + margin-left: 1rem; + font-size: 0.8rem; +} \ No newline at end of file