Compare commits

..

17 Commits

Author SHA1 Message Date
louiscklaw
e83854ed2a update demo-quiz-app, 2025-06-08 19:07:29 +08:00
louiscklaw
6961f058df update demo-list, 2025-06-08 19:06:46 +08:00
louiscklaw
b515337acc update demo-fast-food-app, 2025-06-08 19:06:25 +08:00
louiscklaw
c732d89c34 update demo-ecommerce-example, 2025-06-08 19:06:25 +08:00
louiscklaw
2b71d06c8d update demo-dictionary-app, 2025-06-08 19:06:25 +08:00
louiscklaw
3ec9d87f8b update demo-club-house, 2025-06-08 19:06:25 +08:00
louiscklaw
546fb72732 update demo-banking-ui, 2025-06-08 19:06:05 +08:00
louiscklaw
4303704753 update DemoSkeletonText, 2025-06-08 18:54:06 +08:00
louiscklaw
4bb213ef0c update DemoStickyBottomSheetExample, 2025-06-08 18:47:32 +08:00
louiscklaw
657c652657 updtae Demo2FaExample, 2025-06-08 18:40:35 +08:00
louiscklaw
5be77aae23 update DemoWeatherAppUi, 2025-06-08 16:44:59 +08:00
louiscklaw
ffbe63e421 update DemoStorageExample, 2025-06-08 16:16:40 +08:00
louiscklaw
ba1e718039 update DemoStickyBottomSheetExample, 2025-06-08 15:21:40 +08:00
louiscklaw
8b32d153db update DemoSkeletonText, 2025-06-08 15:04:56 +08:00
louiscklaw
d3e554b218 update DemoSkeletonText, 2025-06-08 11:06:30 +08:00
louiscklaw
5b10977a64 update DemoSkeletonText, 2025-06-08 11:05:39 +08:00
louiscklaw
a40b0fa4b1 update, 2025-06-08 09:20:18 +08:00
102 changed files with 3961 additions and 1485 deletions

View File

@@ -14,9 +14,9 @@ import {
import { Geolocation } from '@capacitor/geolocation';
import { useEffect, useState } from 'react';
import { SkeletonDashboard } from '../components/SkeletonDashboard';
import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard';
import { chevronBackOutline, refreshOutline } from 'ionicons/icons';
import { CurrentWeather } from '../components/CurrentWeather';
import { CurrentWeather } from '../TestComponents/CurrentWeather';
function Tab1() {
const router = useIonRouter();

View File

@@ -10,7 +10,7 @@ import {
IonToolbar,
} from '@ionic/react';
import { useState } from 'react';
import { CurrentWeather } from '../components/CurrentWeather';
import { CurrentWeather } from '../TestComponents/CurrentWeather';
function Tab2() {
const [search, setSearch] = useState('');

View File

@@ -0,0 +1,5 @@
.keypad {
bottom: 0;
position: absolute;
width: 100%;
}

View File

@@ -0,0 +1,118 @@
import { IonRow } from "@ionic/react";
import styles from "./Keypad.module.scss";
import KeypadButton from "./KeypadButton";
const Keypad = (props: any): JSX.Element => {
const { activeIndex, handleClick, handleRemove, amount, correct } = props;
const keypadButtons = [
[
{
value: "1",
handleClick: () => handleClick(activeIndex, 1),
small: false,
remove: false,
},
{
value: "2",
handleClick: () => handleClick(activeIndex, 2),
small: false,
remove: false,
},
{
value: "3",
handleClick: () => handleClick(activeIndex, 3),
small: false,
remove: false,
},
],
[
{
value: "4",
handleClick: () => handleClick(activeIndex, 4),
small: false,
remove: false,
},
{
value: "5",
handleClick: () => handleClick(activeIndex, 5),
small: false,
remove: false,
},
{
value: "6",
handleClick: () => handleClick(activeIndex, 6),
small: false,
remove: false,
},
],
[
{
value: "7",
handleClick: () => handleClick(activeIndex, 7),
small: false,
remove: false,
},
{
value: "8",
handleClick: () => handleClick(activeIndex, 8),
small: false,
remove: false,
},
{
value: "9",
handleClick: () => handleClick(activeIndex, 9),
small: false,
remove: false,
},
],
[
{
value: "Resend",
handleClick: () => handleClick(activeIndex, 1),
small: true,
remove: false,
},
{
value: "0",
handleClick: () => handleClick(activeIndex, 2),
small: false,
remove: false,
},
{
value: "",
handleClick: () => handleRemove(),
small: true,
remove: true,
},
],
];
return (
<div className={`${styles.keypad}`}>
{keypadButtons.map((keypadRow, index) => {
const isDisabled = parseInt(activeIndex) === parseInt(amount);
return (
<IonRow key={`keypadRow_${index}`}>
{keypadRow.map((button, index2) => {
return (
<KeypadButton
correct={correct}
isDisabled={isDisabled}
key={`keypadButton_${index2}`}
value={button.value}
handleClick={button.handleClick}
small={button.small}
remove={button.remove}
/>
);
})}
</IonRow>
);
})}
</div>
);
};
export default Keypad;

View File

@@ -0,0 +1,30 @@
.logo {
height: 4rem;
width: auto;
}
.keypadButton {
--background: none;
--color: black;
font-size: 2rem;
font-weight: 700;
--outline: none;
--border: none;
--box-shadow: none;
padding: none;
margin: none;
--background-hover: rgb(245, 245, 245) !important;
--background-focused: rgb(245, 245, 245) !important;
--background-activated: rgb(245, 245, 245) !important;
}
.smallKeypadButton {
font-size: 1.4rem;
margin-top: 1rem;
}
.keypad {
bottom: 0;
position: absolute;
width: 100%;
}

View File

@@ -0,0 +1,31 @@
import { IonButton, IonCol, IonIcon } from "@ionic/react";
import { backspaceOutline } from "ionicons/icons";
import styles from "./KeypadButton.module.scss";
const KeypadButton = (props: any): JSX.Element => {
const {
small,
value,
remove,
handleClick,
isDisabled = false,
correct,
} = props;
return (
<IonCol size="4" className={styles.keypadButton}>
<IonButton
disabled={(!small || correct) && isDisabled}
className={`${styles.keypadButton} ${
small && styles.smallKeypadButton
}`}
onClick={handleClick}
>
{!remove && value}
{remove && <IonIcon icon={backspaceOutline} />}
</IonButton>
</IonCol>
);
};
export default KeypadButton;

View File

@@ -0,0 +1,38 @@
.keypadInput {
display: flex;
flex-direction: row;
justify-content: center;
align-content: center;
align-items: center;
font-size: 2.5rem;
width: 100%;
min-height: 2.5rem;
background-color: rgb(245, 245, 245);
border-radius: 4px;
color: rgb(207, 207, 207);
transition: 0.2s linear;
}
.active {
background-color: rgba(26, 150, 251, 0.2);
border: 0 !important;
color: white !important;
transition: 0.2s linear;
}
.filled {
color: rgb(151, 151, 151);
transition: 0.2s linear;
}
.incorrect {
background-color: rgba(251, 26, 26, 0.2);
color: rgb(218, 67, 67);
transition: 0.2s linear;
}
.correct {
background-color: rgba(26, 251, 120, 0.2);
color: rgb(67, 218, 112);
transition: 0.2s linear;
}

View File

@@ -0,0 +1,28 @@
import { IonCol } from "@ionic/react";
import styles from "./KeypadInput.module.scss";
const KeypadInput = (props: any): JSX.Element => {
const {
value,
isActive = false,
isFilled = false,
incorrect,
correct,
} = props;
return (
<IonCol size="2">
<div
className={`${styles.keypadInput} ${isActive && styles.active} ${
isFilled && styles.filled
} ${incorrect && styles.incorrect} ${correct && styles.correct}`}
onClick={props.handleChange}
>
{value}
{!isFilled && "0"}
</div>
</IonCol>
);
};
export default KeypadInput;

View File

@@ -0,0 +1,50 @@
import { IonRow } from "@ionic/react";
import { useEffect, useRef } from "react";
import KeypadInput from "./KeypadInput";
const KeypadInputs = (props: any): JSX.Element => {
const { values, activeIndex, incorrect, correct } = props;
const keypadRef = useRef<HTMLIonRowElement>(null);
useEffect(() => {
if (incorrect && keypadRef.current) {
keypadRef.current.classList.add("incorrect");
setTimeout(() => {
if (keypadRef.current) {
keypadRef.current.classList.remove("incorrect");
}
}, 1000);
}
}, [incorrect]);
useEffect(() => {
if (correct) {
if (keypadRef.current) {
keypadRef.current.classList.add("correct");
}
}
}, [correct]);
return (
<IonRow ref={keypadRef} className="ion-justify-content-center">
{values.map((value: string, index: number) => {
const isActive = parseInt(index.toString()) === parseInt(activeIndex);
const isFilled = value !== "" ? true : false;
return (
<KeypadInput
correct={correct}
incorrect={incorrect}
isFilled={isFilled}
isActive={isActive}
value={value}
placeholder="0"
/>
);
})}
</IonRow>
);
};
export default KeypadInputs;

View File

@@ -1,38 +1,19 @@
import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react';
import { IonRouterOutlet, IonTabs } from '@ionic/react';
import { cloudOutline, searchOutline } from 'ionicons/icons';
import { Route, Redirect } from 'react-router';
import Tab1 from './AppPages/Tab1';
import Tab2 from './AppPages/Tab2';
import './style.scss';
import Home from './pages/Home';
function Demo2FaExample() {
return (
<IonTabs>
<IonTabs className="demo-2fa-example">
<IonRouterOutlet>
<Route exact path="/demo-weather-app/tab1">
<Tab1 />
</Route>
<Route exact path="/demo-weather-app/tab2">
<Tab2 />
<Route exact path="/demo-2fa-example/home">
<Home />
</Route>
<Redirect exact path="/demo-weather-app" to="/demo-weather-app/tab1" />
<Redirect exact path="/demo-2fa-example" to="/demo-2fa-example/home" />
</IonRouterOutlet>
{/* */}
<IonTabBar slot="bottom">
<IonTabButton tab="tab1" href="/demo-weather-app/tab1">
<IonIcon icon={cloudOutline} />
<IonLabel>Dashboard</IonLabel>
</IonTabButton>
<IonTabButton tab="tab2" href="/demo-weather-app/tab2">
<IonIcon icon={searchOutline} />
<IonLabel>Search</IonLabel>
</IonTabButton>
</IonTabBar>
</IonTabs>
);
}

View File

@@ -0,0 +1,30 @@
.logo {
height: 4rem;
width: auto;
}
.incorrect {
color: rgb(218, 67, 67);
}
.successContainer {
}
.successText {
background-color: rgba(26, 251, 120, 0.2);
color: rgb(67, 218, 112);
padding: 1rem;
}
.successContinue {
font-weight: 700;
display: flex;
flex-direction: row;
justify-content: center;
align-content: center;
align-items: center;
}
.successContinue ion-icon {
margin-top: 0.2rem;
}

View File

@@ -0,0 +1,135 @@
import {
IonButton,
IonButtons,
IonCol,
IonContent,
IonGrid,
IonHeader,
IonIcon,
IonImg,
IonPage,
IonRow,
IonToolbar,
useIonRouter,
} from '@ionic/react';
import { arrowForwardOutline, chevronBackOutline } from 'ionicons/icons';
import styles from './Home.module.scss';
import KeypadInputs from '../components/KeypadInputs';
import Keypad from '../components/Keypad';
import { JSX, useEffect, useRef, useState } from 'react';
const Home = (): JSX.Element => {
const correctCode = [5, 9, 2, 5];
const [keypadValues, setKeypadValues] = useState(['', '', '', '']);
const [activeIndex, setActiveIndex] = useState(0);
const successRef = useRef<HTMLIonRowElement>(null);
const [incorrect, setIncorrect] = useState(false);
const [correct, setCorrect] = useState(false);
const tempValues: { [key: string]: any } = {};
const handleClick = (index: number, value: any) => {
const stringKey = index.toString();
tempValues[stringKey] = value;
setKeypadValues(value);
setActiveIndex((activeIndex) => activeIndex + 1);
};
const handleRemove = () => {
const tempValues = [...keypadValues];
tempValues[activeIndex - 1] = '';
setKeypadValues(tempValues);
activeIndex > 0 && setActiveIndex((activeIndex) => activeIndex - 1);
setIncorrect(false);
setCorrect(false);
};
const router = useIonRouter();
function handleBackClick() {
router.goBack();
}
useEffect(() => {
if (parseInt(activeIndex.toString()) === parseInt(keypadValues.length.toString())) {
var error = false;
keypadValues.forEach((value, index) => {
if (parseInt(value) !== parseInt(correctCode[index].toString())) {
error = true;
return false;
}
});
if (error) {
setIncorrect(true);
} else {
setCorrect(true);
setTimeout(() => {
if (successRef.current) {
successRef.current.classList.remove('hidden');
successRef.current.classList.add('success');
}
}, 900);
}
}
}, [activeIndex]);
return (
<IonPage>
<IonToolbar>
<IonHeader></IonHeader>
<IonButtons slot="start">
<IonButton shape={'round'} onClick={() => handleBackClick()}>
<IonIcon icon={chevronBackOutline} color="primary" />
</IonButton>
</IonButtons>
</IonToolbar>
<IonContent fullscreen>
<IonGrid className="ion-text-center ion-padding-top">
<IonRow>
<IonCol size="12">
<IonImg src="/assets/icon/favicon.png" className={styles.logo} />
<h1>Verification required</h1>
<p>Enter your 4 digit verification code</p>
</IonCol>
</IonRow>
<IonRow ref={successRef} className="ion-justify-content-center hidden">
<IonCol size="12" className={styles.successContainer}>
<p className={styles.successText}>
Awesome! You may continue.
<br />
<span className={styles.successContinue}>
Continue&nbsp;&nbsp;
<IonIcon icon={arrowForwardOutline} />
</span>
</p>
</IonCol>
</IonRow>
<KeypadInputs
incorrect={incorrect}
correct={correct}
values={keypadValues}
activeIndex={activeIndex}
/>
{incorrect && <p className={styles.incorrect}>Wrong code entered</p>}
<Keypad
handleRemove={handleRemove}
handleClick={handleClick}
activeIndex={activeIndex}
amount={keypadValues.length}
correct={correct}
/>
</IonGrid>
</IonContent>
</IonPage>
);
};
export default Home;

View File

@@ -1,103 +0,0 @@
#about-page {
ion-toolbar {
position: absolute;
top: 0;
left: 0;
right: 0;
--background: transparent;
--color: white;
}
ion-toolbar ion-back-button,
ion-toolbar ion-button,
ion-toolbar ion-menu-button {
--color: white;
}
.about-header {
position: relative;
width: 100%;
height: 30%;
}
.about-header .about-image {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-position: center;
background-size: cover;
background-repeat: no-repeat;
opacity: 0;
transition: opacity 500ms ease-in-out;
}
.about-header .madison {
background-image: url('/assets/WeatherDemo/img/about/madison.jpg');
}
.about-header .austin {
background-image: url('/assets/WeatherDemo/img/about/austin.jpg');
}
.about-header .chicago {
background-image: url('/assets/WeatherDemo/img/about/chicago.jpg');
}
.about-header .seattle {
background-image: url('/assets/WeatherDemo/img/about/seattle.jpg');
}
.about-info {
position: relative;
margin-top: -10px;
border-radius: 10px;
background: var(--ion-background-color, #fff);
z-index: 2; // display rounded border above header image
}
.about-info h3 {
margin-top: 0;
}
.about-info ion-list {
padding-top: 0;
}
.about-info p {
line-height: 130%;
color: var(--ion-color-dark);
}
.about-info ion-icon {
margin-inline-end: 32px;
}
/*
* iOS Only
*/
.ios .about-info {
--ion-padding: 19px;
}
.ios .about-info h3 {
font-weight: 700;
}
}
#date-input-popover {
--offset-y: -var(--ion-safe-area-bottom);
--max-width: 90%;
--width: 336px;
}

View File

@@ -0,0 +1,249 @@
.demo-2fa-example {
/* Ionic Variables and Theming. For more info, please see:
http://ionicframework.com/docs/theming/ */
* {
font-family: 'Lato', sans-serif;
}
.hidden {
display: none;
transition: 0.2s linear;
}
/** 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;
}
.incorrect {
-webkit-animation: incorrect-animation 0.9s both;
animation: incorrect-animation 0.9s both;
}
@-webkit-keyframes incorrect-animation {
0% {
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
}
30% {
-webkit-transform: scale3d(1.25, 0.75, 1);
transform: scale3d(1.25, 0.75, 1);
}
40% {
-webkit-transform: scale3d(0.75, 1.25, 1);
transform: scale3d(0.75, 1.25, 1);
}
50% {
-webkit-transform: scale3d(1.15, 0.85, 1);
transform: scale3d(1.15, 0.85, 1);
}
65% {
-webkit-transform: scale3d(0.95, 1.05, 1);
transform: scale3d(0.95, 1.05, 1);
}
75% {
-webkit-transform: scale3d(1.05, 0.95, 1);
transform: scale3d(1.05, 0.95, 1);
}
100% {
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
}
}
@keyframes incorrect-animation {
0% {
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
}
30% {
-webkit-transform: scale3d(1.25, 0.75, 1);
transform: scale3d(1.25, 0.75, 1);
}
40% {
-webkit-transform: scale3d(0.75, 1.25, 1);
transform: scale3d(0.75, 1.25, 1);
}
50% {
-webkit-transform: scale3d(1.15, 0.85, 1);
transform: scale3d(1.15, 0.85, 1);
}
65% {
-webkit-transform: scale3d(0.95, 1.05, 1);
transform: scale3d(0.95, 1.05, 1);
}
75% {
-webkit-transform: scale3d(1.05, 0.95, 1);
transform: scale3d(1.05, 0.95, 1);
}
100% {
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
}
}
.correct {
-webkit-animation: correct-animation 1s ease-in both;
animation: correct-animation 1s ease-in both;
}
@-webkit-keyframes correct-animation {
0% {
-webkit-transform: translateY(0) rotateX(0) scale(1);
transform: translateY(0) rotateX(0) scale(1);
-webkit-transform-origin: 50% 1400px;
transform-origin: 50% 1400px;
opacity: 1;
}
100% {
-webkit-transform: translateY(-600px) rotateX(-30deg) scale(0);
transform: translateY(-600px) rotateX(-30deg) scale(0);
-webkit-transform-origin: 50% 100%;
transform-origin: 50% 100%;
opacity: 1;
}
}
@keyframes correct-animation {
0% {
-webkit-transform: translateY(0) rotateX(0) scale(1);
transform: translateY(0) rotateX(0) scale(1);
-webkit-transform-origin: 50% 1400px;
transform-origin: 50% 1400px;
opacity: 1;
}
100% {
-webkit-transform: translateY(-600px) rotateX(-30deg) scale(0);
transform: translateY(-600px) rotateX(-30deg) scale(0);
-webkit-transform-origin: 50% 100%;
transform-origin: 50% 100%;
opacity: 1;
}
}
.success {
-webkit-animation: success-animation 0.7s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
animation: success-animation 0.7s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
}
@-webkit-keyframes success-animation {
0% {
-webkit-transform: translateY(-600px) rotateX(-30deg) scale(0);
transform: translateY(-600px) rotateX(-30deg) scale(0);
-webkit-transform-origin: 50% 100%;
transform-origin: 50% 100%;
opacity: 0;
}
100% {
-webkit-transform: translateY(0) rotateX(0) scale(1);
transform: translateY(0) rotateX(0) scale(1);
-webkit-transform-origin: 50% 1400px;
transform-origin: 50% 1400px;
opacity: 1;
}
}
@keyframes success-animation {
0% {
-webkit-transform: translateY(-600px) rotateX(-30deg) scale(0);
transform: translateY(-600px) rotateX(-30deg) scale(0);
-webkit-transform-origin: 50% 100%;
transform-origin: 50% 100%;
opacity: 0;
}
100% {
-webkit-transform: translateY(0) rotateX(0) scale(1);
transform: translateY(0) rotateX(0) scale(1);
-webkit-transform-origin: 50% 1400px;
transform-origin: 50% 1400px;
opacity: 1;
}
}
}

View File

@@ -13,8 +13,9 @@ import {
IonThumbnail,
} from '@ionic/react';
import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons';
import React from 'react';
export const SkeletonDashboard = () => (
export const SkeletonDashboard = (): React.JSX.Element => (
<IonGrid>
<IonCard>
<IonCardContent className="ion-text-center">

View File

@@ -1,19 +1,19 @@
import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react';
import { IonRouterOutlet, IonTabs } from '@ionic/react';
import { cloudOutline, searchOutline } from 'ionicons/icons';
import { Route, Redirect } from 'react-router';
import Tab1 from './AppPages/Tab1';
import Tab2 from './AppPages/Tab2';
// import Tab1 from './AppPages/Tab1';
// import Tab2 from './AppPages/Tab2';
import Home from './pages/Home.jsx';
import Home from './pages/Home';
import Account from './pages/Account';
import AddCard from './pages/AddCard';
import AddTransaction from './pages/AddTransaction';
import './style.scss';
import React from 'react';
function DemoBankingUi() {
function DemoBankingUi(): React.JSX.Element {
return (
<IonTabs className="demo-banking-ui">
<IonRouterOutlet>

View File

@@ -1,5 +1,17 @@
import { useRef, useState } from 'react';
import { IonButton, IonButtons, IonContent, IonGrid, IonHeader, IonIcon, IonPage, IonTitle, IonToolbar, useIonRouter, useIonViewDidEnter } from '@ionic/react';
import {
IonButton,
IonButtons,
IonContent,
IonGrid,
IonHeader,
IonIcon,
IonPage,
IonTitle,
IonToolbar,
useIonRouter,
useIonViewDidEnter,
} from '@ionic/react';
import styles from './Home.module.css';
import { AccountStore } from '../data/AccountStore';
import CardSlide from '../components/CardSlide';
@@ -22,7 +34,7 @@ const Home = () => {
const [mainColor, setMainColor] = useState(cards[0].color);
const [slideSpace, setSlideSpace] = useState(10);
const slidesRef = useRef();
const slidesRef = useRef(null);
useIonViewDidEnter(() => {
setSlideSpace(0);
@@ -38,7 +50,9 @@ const Home = () => {
document.getElementById(`slide_${swiperIndex}_balance`).classList.add('animate__headShake');
setTimeout(() => {
document.getElementById(`slide_${swiperIndex}_balance`).classList.remove('animate__headShake');
document
.getElementById(`slide_${swiperIndex}_balance`)
.classList.remove('animate__headShake');
}, 1000);
};
@@ -47,9 +61,13 @@ const Home = () => {
const swiperIndex = swiper.activeIndex;
if (touched) {
document.getElementById(`slide_${swiperIndex}_transactions`).classList.add('animate__fadeOut');
document
.getElementById(`slide_${swiperIndex}_transactions`)
.classList.add('animate__fadeOut');
} else {
document.getElementById(`slide_${swiperIndex}_transactions`).classList.remove('animate__fadeOut');
document
.getElementById(`slide_${swiperIndex}_transactions`)
.classList.remove('animate__fadeOut');
document.getElementById(`slide_${swiperIndex}_transactions`).classList.add('animate__fadeIn');
}
};
@@ -70,7 +88,11 @@ const Home = () => {
routerLink="/demo-banking-ui/account"
className={stylesS.toolbarAvatar}
>
<img alt="toolbar avatar" className={stylesS.toolbarAvatarImage} src={profile.avatar} />
<img
alt="toolbar avatar"
className={stylesS.toolbarAvatarImage}
src={profile.avatar}
/>
</IonButton>
</IonButtons>
@@ -82,7 +104,11 @@ const Home = () => {
</IonButton>
{/* */}
<IonButton>
<IonIcon color="light" icon={searchOutline} style={{ backgroundColor: mainColor, borderRadius: '500px', padding: '0.2rem' }} />
<IonIcon
color="light"
icon={searchOutline}
style={{ backgroundColor: mainColor, borderRadius: '500px', padding: '0.2rem' }}
/>
</IonButton>
</IonButtons>
</IonToolbar>
@@ -101,7 +127,11 @@ const Home = () => {
>
{cards.map((card, index) => {
return (
<SwiperSlide key={`slide_${index}`} id={`slide_${index}`} className={stylesS.customSlide}>
<SwiperSlide
key={`slide_${index}`}
id={`slide_${index}`}
className={stylesS.customSlide}
>
<CardSlide key={index} card={card} profile={profile} index={index} />
</SwiperSlide>
);

View File

@@ -23,7 +23,7 @@ import { TalkCard } from '../components/TalkCard';
import { useRef } from 'react';
const Tab1 = () => {
const pageRef = useRef();
const pageRef = useRef(null);
const talks = useStoreState(TalkStore, getTalks);
const router = useIonRouter();

View File

@@ -24,7 +24,7 @@ import { getFavourites, getSearchCount } from '../store/Selectors';
const Tab1 = () => {
const router = useIonRouter();
const pageRef = useRef();
const pageRef = useRef(null);
const favourites = useStoreState(WordStore, getFavourites);
const searchCount = useStoreState(WordStore, getSearchCount);

View File

@@ -17,7 +17,7 @@ import { WordStore } from '../store';
import { searchWord } from '../utils';
const Tab2 = () => {
const pageRef = useRef();
const pageRef = useRef(null);
const [searchTerm, setSearchTerm] = useState('');
const [searchResult, setSearchResult] = useState(false);
const [animatedClass, setAnimatedClass] = useState('');

View File

@@ -7,7 +7,7 @@ import { WordStore } from '../store';
import { getFavourites } from '../store/Selectors';
const Tab3 = () => {
const pageRef = useRef();
const pageRef = useRef(null);
const favourites = useStoreState(WordStore, getFavourites);
const [animatedClass, setAnimatedClass] = useState('animate__slideInLeft');

View File

@@ -0,0 +1,72 @@
.categoryPage ion-toolbar {
--border-style: none;
}
.categoryCard {
display: flex;
flex-direction: column;
justify-content: center;
align-content: center;
align-items: center;
/* min-height: 20rem !important; */
}
.productCardActions {
display: flex;
flex-direction: row;
justify-content: space-between;
width: 100%;
margin-bottom: 1rem;
}
.productCardAction {
font-size: 1.1rem;
}
.productCardHeader {
min-height: 17rem;
}
.productCardHeader p {
font-size: 0.8rem;
padding: 0;
margin: 0;
margin-top: 0.75rem;
}
.categoryCardContent {
display: flex;
flex-direction: column;
}
.categoryCardContent ion-button {
height: 1.5rem;
font-size: 0.8rem;
}
.categoryCardContent p {
font-size: 0.8rem;
padding: 0;
margin: 0;
}
.categoryCard img {
/* border-radius: 5px; */
/* padding: 1rem; */
}
.productPrice {
display: flex;
flex-direction: row;
}

View File

@@ -0,0 +1,105 @@
import { IonButton, IonCard, IonCardContent, IonCardHeader, IonCol, IonIcon } from '@ionic/react';
import { arrowRedoOutline, cart, cartOutline, heart, heartOutline } from 'ionicons/icons';
import { useEffect, useRef, useState } from 'react';
import { addToCart } from '../data/CartStore';
import { addToFavourites, FavouritesStore } from '../data/FavouritesStore';
import styles from './ProductCard.module.css';
const ProductCard = (props): React.FC => {
const { product, category, index, cartRef } = props;
const favourites = FavouritesStore.useState((s) => s.product_ids);
const productCartRef = useRef();
const productFavouriteRef = useRef();
const [isFavourite, setIsFavourite] = useState(false);
useEffect(() => {
const tempIsFavourite = favourites.find((f) => f === `${category.slug}/${product.id}`);
setIsFavourite(tempIsFavourite ? true : false);
}, [props.product, favourites]);
const addProductToFavourites = (e, categorySlug, productID) => {
e.preventDefault();
e.stopPropagation();
addToFavourites(categorySlug, productID);
productFavouriteRef.current.style.display = '';
productFavouriteRef.current.classList.add('animate__fadeOutTopRight');
setTimeout(() => {
if (productCartRef.current) {
productFavouriteRef.current.classList.remove('animate__fadeOutTopRight');
productFavouriteRef.current.style.display = 'none';
}
}, 500);
};
const addProductToCart = (e, categorySlug, productID) => {
e.preventDefault();
e.stopPropagation();
productCartRef.current.style.display = '';
productCartRef.current.classList.add('animate__fadeOutUp');
setTimeout(() => {
cartRef.current.classList.add('animate__tada');
addToCart(categorySlug, productID);
setTimeout(() => {
cartRef.current.classList.remove('animate__tada');
productCartRef.current.style.display = 'none';
}, 500);
}, 500);
};
return (
<IonCol size="6" key={`category_product_list_${index}`}>
<IonCard
routerLink={`/category/${category.slug}/${product.id}`}
className={styles.categoryCard}
>
<IonCardHeader className={styles.productCardHeader}>
<div className={styles.productCardActions}>
<IonIcon
className={styles.productCardAction}
color={isFavourite ? 'danger' : 'medium'}
icon={isFavourite ? heart : heartOutline}
onClick={(e) => addProductToFavourites(e, category.slug, product.id)}
/>
<IonIcon
ref={productFavouriteRef}
style={{ position: 'absolute', display: 'none' }}
className={`${styles.productCardAction} animate__animated`}
color="danger"
icon={heart}
/>
<IonIcon className={styles.productCardAction} size="medium" icon={arrowRedoOutline} />
</div>
<img src={product.image} alt="product pic" />
<p className="ion-text-wrap">{product.name}</p>
</IonCardHeader>
<IonCardContent className={styles.categoryCardContent}>
<div className={styles.productPrice}>
<IonButton style={{ width: '100%' }} color="light">
{product.price}
</IonButton>
<IonButton color="dark" onClick={(e) => addProductToCart(e, category.slug, product.id)}>
<IonIcon icon={cartOutline} />
</IonButton>
<IonIcon
ref={productCartRef}
icon={cart}
color="dark"
style={{ position: 'absolute', display: 'none', fontSize: '3rem' }}
className="animate__animated"
/>
</div>
</IonCardContent>
</IonCard>
</IonCol>
);
};
export default ProductCard;

View File

@@ -0,0 +1,18 @@
import { Store } from 'pullstate';
export const CartStore = new Store({
total: 0,
product_ids: [],
});
export const addToCart = (categorySlug, productID) => {
CartStore.update((s) => {
s.product_ids = [...s.product_ids, `${categorySlug}/${parseInt(productID)}`];
});
};
export const removeFromCart = (productIndex) => {
CartStore.update((s) => {
s.product_ids.splice(productIndex, 1);
});
};

View File

@@ -0,0 +1,17 @@
import { Store } from "pullstate";
export const FavouritesStore = new Store({
total: 0,
product_ids: []
});
export const addToFavourites = (categorySlug, productID) => {
FavouritesStore.update(s => {
if (s.product_ids.find(id => id === `${ categorySlug }/${ parseInt(productID) }`)) {
s.product_ids = s.product_ids.filter(id => id !== `${ categorySlug }/${ parseInt(productID) }`);
} else {
s.product_ids = [ ...s.product_ids, `${ categorySlug }/${ parseInt(productID) }` ];
}
});
}

View File

@@ -0,0 +1,6 @@
import { Store } from "pullstate";
export const ProductStore = new Store({
products: []
});

View File

@@ -0,0 +1,57 @@
import { ProductStore } from './ProductStore';
export const fetchData = async () => {
const json = [
'beds.json',
'armchairs.json',
'coffee_tables.json',
'cushions.json',
'floor_lamps.json',
'office_chairs.json',
];
var products = [];
json.forEach(async (category) => {
const products = await fetchProducts(category);
let categoryName = category.replace('.json', '');
categoryName = categoryName.replace('_', ' ');
categoryName = uppercaseWords(categoryName);
const productCategory = {
name: categoryName,
slug: category.replace('.json', ''),
cover: products[6].image,
products,
};
ProductStore.update((s) => {
s.products = [...s.products, productCategory];
});
});
return products;
};
const fetchProducts = async (category) => {
const response = await fetch(`products/${category}`);
const data = await response.json();
// Set a product id
await data.forEach((d, i) => {
d.id = i + 1;
});
return data;
};
const uppercaseWords = (words) => {
words = words
.toLowerCase()
.split(' ')
.map((s) => s.charAt(0).toUpperCase() + s.substring(1))
.join(' ');
return words;
};

View File

@@ -3,23 +3,48 @@ import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs }
import { cloudOutline, searchOutline } from 'ionicons/icons';
import { Route, Redirect } from 'react-router';
import Tab1 from './AppPages/Tab1';
import Tab2 from './AppPages/Tab2';
// import Tab1 from './AppPages/Tab1';
// import Tab2 from './AppPages/Tab2';
import Home from './pages/Home';
import { fetchData } from './data/fetcher';
import CategoryProducts from './pages/CategoryProducts';
import Product from './pages/Product';
import FavouriteProducts from './pages/FavouriteProducts';
import CartProducts from './pages/CartProducts';
import './style.scss';
import React, { useEffect } from 'react';
function DemoEcommerceExample(): React.JSX.Element {
useEffect(() => {
fetchData();
}, []);
function DemoEcommerceExample() {
return (
<IonTabs>
<IonRouterOutlet>
<Route exact path="/demo-ecommerce-example/tab1">
<Tab1 />
</Route>
<Route exact path="/demo-ecommerce-example/tab2">
<Tab2 />
<Route path="/demo-ecommerce-example/home" exact={true}>
<Home />
</Route>
<Redirect exact path="/demo-ecommerce-example" to="/demo-ecommerce-example/tab1" />
<Route path="/demo-ecommerce-example/favourites" exact>
<FavouriteProducts />
</Route>
<Route path="/demo-ecommerce-example/cart" exact>
<CartProducts />
</Route>
<Route path="/demo-ecommerce-example/category/:slug" exact>
<CategoryProducts />
</Route>
<Route path="/demo-ecommerce-example/category/:slug/:id" exact>
<Product />
</Route>
<Redirect exact path="/demo-ecommerce-example" to="/demo-ecommerce-example/home" />
</IonRouterOutlet>
{/* */}

View File

@@ -0,0 +1,4 @@
declare module '*.module.css' {
const classes: { readonly [key: string]: string };
export default classes;
}

View File

@@ -0,0 +1,31 @@
.cartCheckout {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
align-content: center;
margin: 1rem;
}
.cartFooter {
border-top: 2px solid rgb(200, 200, 200);
background-color: white;
}
.cartCheckout ion-card-subtitle {
font-size: 1.3rem;
}
.cartItem ion-avatar {
height: 4rem;
width: 4rem;
}
.cartSlider:not(:nth-child(1)) {
border-top: 2px solid rgb(236, 236, 236);
}
.cartActions {
display: flex;
flex-direction: column;
}

View File

@@ -0,0 +1,161 @@
import {
IonAvatar,
IonBadge,
IonButton,
IonButtons,
IonCardSubtitle,
IonCol,
IonContent,
IonFooter,
IonHeader,
IonIcon,
IonImg,
IonItem,
IonItemOption,
IonItemOptions,
IonItemSliding,
IonLabel,
IonList,
IonNote,
IonPage,
IonRow,
IonTitle,
IonToolbar,
} from '@ionic/react';
import { cart, checkmarkSharp, chevronBackOutline, trashOutline } from 'ionicons/icons';
import React, { useEffect, useRef, useState } from 'react';
import { CartStore, removeFromCart } from '../data/CartStore';
import { ProductStore } from '../data/ProductStore';
import styles from './CartProducts.module.css';
const CartProducts = (): React.JSX.Element => {
const cartRef = useRef(null);
const products = ProductStore.useState((s) => s.products);
const shopCart = CartStore.useState((s) => s.product_ids);
const [cartProducts, setCartProducts] = useState([]);
const [amountLoaded, setAmountLoaded] = useState(6);
const [total, setTotal] = useState(0);
useEffect(() => {
const getCartProducts = () => {
setCartProducts([]);
setTotal(0);
shopCart.forEach((product) => {
var favouriteParts = product.split('/');
var categorySlug = favouriteParts[0];
var productID = favouriteParts[1];
const tempCategory = products.filter((p) => p.slug === categorySlug)[0];
const tempProduct = tempCategory.products.filter(
(p) => parseInt(p.id) === parseInt(productID)
)[0];
const tempCartProduct = {
category: tempCategory,
product: tempProduct,
};
setTotal((prevTotal) => prevTotal + parseInt(tempProduct.price.replace('£', '')));
setCartProducts((prevSearchResults) => [...prevSearchResults, tempCartProduct]);
});
};
getCartProducts();
}, [shopCart]);
const fetchMore = async (e) => {
// Increment the amount loaded by 6 for the next iteration
setAmountLoaded((prevAmount) => prevAmount + 6);
e.target.complete();
};
const removeProductFromCart = async (index) => {
removeFromCart(index);
};
return (
<IonPage id="category-page" className={styles.categoryPage}>
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<IonButton color="dark" routerLink="/" routerDirection="back">
<IonIcon color="dark" icon={chevronBackOutline} />
&nbsp;Categories
</IonButton>
</IonButtons>
<IonTitle>Cart</IonTitle>
<IonButtons slot="end">
<IonBadge color="dark">{shopCart.length}</IonBadge>
<IonButton color="dark">
<IonIcon ref={cartRef} className="animate__animated" icon={cart} />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonRow className="ion-text-center ion-margin-top">
<IonCol size="12">
<IonNote>
{cartProducts && cartProducts.length}{' '}
{cartProducts.length > 1 || cartProducts.length === 0 ? ' products' : ' product'}{' '}
found
</IonNote>
</IonCol>
</IonRow>
<IonList>
{cartProducts &&
cartProducts.map((product, index) => {
if (index <= amountLoaded) {
return (
<IonItemSliding className={styles.cartSlider}>
<IonItem key={index} lines="none" detail={false} className={styles.cartItem}>
<IonAvatar>
<IonImg src={product.product.image} />
</IonAvatar>
<IonLabel className="ion-padding-start ion-text-wrap">
<p>{product.category.name}</p>
<h4>{product.product.name}</h4>
</IonLabel>
<div className={styles.cartActions}>
<IonBadge color="dark">{product.product.price}</IonBadge>
</div>
</IonItem>
<IonItemOptions side="end">
<IonItemOption
color="danger"
style={{ paddingLeft: '1rem', paddingRight: '1rem' }}
onClick={() => removeProductFromCart(index)}
>
<IonIcon icon={trashOutline} />
</IonItemOption>
</IonItemOptions>
</IonItemSliding>
);
}
})}
</IonList>
</IonContent>
<IonFooter className={styles.cartFooter}>
<div className={styles.cartCheckout}>
<IonCardSubtitle>£{total.toFixed(2)}</IonCardSubtitle>
<IonButton color="dark">
<IonIcon icon={checkmarkSharp} />
&nbsp;Checkout
</IonButton>
</div>
</IonFooter>
</IonPage>
);
};
export default CartProducts;

View File

@@ -0,0 +1,10 @@
.categoryPage ion-toolbar {
--border-style: none;
}
.search {
--background: rgb(240, 240, 240);
--color: black;
}

View File

@@ -0,0 +1,134 @@
import {
IonBadge,
IonButton,
IonButtons,
IonCol,
IonContent,
IonGrid,
IonHeader,
IonIcon,
IonInfiniteScroll,
IonInfiniteScrollContent,
IonNote,
IonPage,
IonRow,
IonSearchbar,
IonTitle,
IonToolbar,
} from '@ionic/react';
import { cart, chevronBackOutline, searchOutline } from 'ionicons/icons';
import { useEffect, useRef, useState } from 'react';
import { useParams } from 'react-router';
import ProductCard from '../components/ProductCard';
import { CartStore } from '../data/CartStore';
import { ProductStore } from '../data/ProductStore';
import styles from './CategoryProducts.module.css';
const CategoryProducts = () => {
const params = useParams();
const cartRef = useRef();
const products = ProductStore.useState((s) => s.products);
const shopCart = CartStore.useState((s) => s.product_ids);
const [category, setCategory] = useState({});
const [searchResults, setsearchResults] = useState([]);
const [amountLoaded, setAmountLoaded] = useState(6);
useEffect(() => {
const categorySlug = params.slug;
const tempCategory = products.filter((p) => p.slug === categorySlug)[0];
setCategory(tempCategory);
setsearchResults(tempCategory.products);
}, [params.slug]);
const fetchMore = async (e) => {
// Increment the amount loaded by 6 for the next iteration
setAmountLoaded((prevAmount) => prevAmount + 6);
e.target.complete();
};
const search = async (e) => {
const searchVal = e.target.value;
if (searchVal !== '') {
const tempResults = category.products.filter((p) =>
p.name.toLowerCase().includes(searchVal.toLowerCase())
);
setsearchResults(tempResults);
} else {
setsearchResults(category.products);
}
};
return (
<IonPage id="category-page" className={styles.categoryPage}>
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<IonButton color="dark" text={category.name} routerLink="/" routerDirection="back">
<IonIcon color="dark" icon={chevronBackOutline} />
&nbsp;Categories
</IonButton>
</IonButtons>
<IonTitle>{category && category.name}</IonTitle>
<IonButtons slot="end">
<IonBadge color="dark">{shopCart.length}</IonBadge>
<IonButton color="dark" routerLink="/cart">
<IonIcon ref={cartRef} className="animate__animated" icon={cart} />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonSearchbar
className={styles.search}
onKeyUp={search}
placeholder="Try 'high back'"
searchIcon={searchOutline}
animated={true}
/>
<IonGrid>
<IonRow className="ion-text-center">
<IonCol size="12">
<IonNote>
{searchResults && searchResults.length}{' '}
{searchResults.length > 1 || searchResults.length === 0 ? ' products' : ' product'}{' '}
found
</IonNote>
</IonCol>
</IonRow>
<IonRow>
{searchResults &&
searchResults.map((product, index) => {
if (index <= amountLoaded && product.image) {
return (
<ProductCard
key={`category_product_${index}`}
product={product}
index={index}
cartRef={cartRef}
category={category}
/>
);
}
})}
</IonRow>
</IonGrid>
<IonInfiniteScroll threshold="100px" onIonInfinite={fetchMore}>
<IonInfiniteScrollContent
loadingSpinner="bubbles"
loadingText="Fetching more..."
></IonInfiniteScrollContent>
</IonInfiniteScroll>
</IonContent>
</IonPage>
);
};
export default CategoryProducts;

View File

@@ -0,0 +1,131 @@
import {
IonBadge,
IonButton,
IonButtons,
IonCol,
IonContent,
IonGrid,
IonHeader,
IonIcon,
IonInfiniteScroll,
IonInfiniteScrollContent,
IonNote,
IonPage,
IonRow,
IonTitle,
IonToolbar,
} from '@ionic/react';
import { cart, chevronBackOutline } from 'ionicons/icons';
import { useEffect, useRef, useState } from 'react';
import ProductCard from '../components/ProductCard';
import { CartStore } from '../data/CartStore';
import { FavouritesStore } from '../data/FavouritesStore';
import { ProductStore } from '../data/ProductStore';
import styles from './CategoryProducts.module.css';
const FavouriteProducts = () => {
const cartRef = useRef(null);
const products = ProductStore.useState((s) => s.products);
const favourites = FavouritesStore.useState((s) => s.product_ids);
const shopCart = CartStore.useState((s) => s.product_ids);
const [searchResults, setSearchResults] = useState([]);
const [amountLoaded, setAmountLoaded] = useState(6);
useEffect(() => {
const getFavourites = () => {
setSearchResults([]);
favourites.forEach((favourite) => {
var favouriteParts = favourite.split('/');
var categorySlug = favouriteParts[0];
var productID = favouriteParts[1];
const tempCategory = products.filter((p) => p.slug === categorySlug)[0];
const tempProduct = tempCategory.products.filter(
(p) => parseInt(p.id) === parseInt(productID)
)[0];
const tempFavourite = {
category: tempCategory,
product: tempProduct,
};
setSearchResults((prevSearchResults) => [...prevSearchResults, tempFavourite]);
});
};
getFavourites();
}, [favourites]);
const fetchMore = async (e) => {
// Increment the amount loaded by 6 for the next iteration
setAmountLoaded((prevAmount) => prevAmount + 6);
e.target.complete();
};
return (
<IonPage id="category-page" className={styles.categoryPage}>
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<IonButton color="dark" routerLink="/" routerDirection="back">
<IonIcon color="dark" icon={chevronBackOutline} />
&nbsp;Categories
</IonButton>
</IonButtons>
<IonTitle>Favourites</IonTitle>
<IonButtons slot="end">
<IonBadge color="dark">{shopCart.length}</IonBadge>
<IonButton color="dark">
<IonIcon ref={cartRef} className="animate__animated" icon={cart} />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonGrid>
<IonRow className="ion-text-center">
<IonCol size="12">
<IonNote>
{searchResults && searchResults.length}{' '}
{searchResults.length > 1 || searchResults.length === 0
? ' favourites'
: ' favourite'}{' '}
found
</IonNote>
</IonCol>
</IonRow>
<IonRow>
{searchResults &&
searchResults.map((product, index) => {
if (index <= amountLoaded) {
return (
<ProductCard
key={`category_product_${index}`}
product={product.product}
index={index}
cartRef={cartRef}
category={product.category}
/>
);
}
})}
</IonRow>
</IonGrid>
<IonInfiniteScroll threshold="100px" onIonInfinite={fetchMore}>
<IonInfiniteScrollContent
loadingSpinner="bubbles"
loadingText="Fetching more..."
></IonInfiniteScrollContent>
</IonInfiniteScroll>
</IonContent>
</IonPage>
);
};
export default FavouriteProducts;

View File

@@ -0,0 +1,42 @@
.homePage ion-toolbar {
--border-style: none;
}
.logo {
margin-top: 0.25rem;
color: var(--ion-color-primary);
}
.categoryCard,
.categoryCardContent {
display: flex;
flex-direction: column;
justify-content: center;
align-content: center;
align-items: center;
}
.categoryCardContent ion-button {
height: 1.5rem;
font-size: 0.8rem;
}
.categoryCardContent {
background-color: rgb(238, 238, 238);
}
.categoryCardContent ion-card-subtitle {
/* color: rgb(78, 78, 78); */
}
.categoryCard img {
/* border-radius: 5px; */
padding: 1rem;
}

View File

@@ -0,0 +1,88 @@
import { useState } from 'react';
import {
IonBadge,
IonButton,
IonButtons,
IonCard,
IonCardContent,
IonCardSubtitle,
IonCol,
IonContent,
IonGrid,
IonHeader,
IonIcon,
IonPage,
IonRow,
IonTitle,
IonToolbar,
} from '@ionic/react';
import styles from './Home.module.css';
import { cart, heart } from 'ionicons/icons';
import { ProductStore } from '../data/ProductStore';
import { FavouritesStore } from '../data/FavouritesStore';
import { CartStore } from '../data/CartStore';
const Home = () => {
const products = ProductStore.useState((s) => s.products);
const favourites = FavouritesStore.useState((s) => s.product_ids);
const shopCart = CartStore.useState((s) => s.product_ids);
return (
<IonPage id="home-page" className={styles.homePage}>
<IonHeader>
<IonToolbar>
<IonTitle>Categories</IonTitle>
<IonButtons slot="start" className="ion-padding-start">
<IonCardSubtitle className={styles.logo}>Ionic Furniture</IonCardSubtitle>
</IonButtons>
<IonButtons slot="end">
<IonBadge color="danger">{favourites.length}</IonBadge>
<IonButton color="danger" routerLink="/favourites">
<IonIcon icon={heart} />
</IonButton>
<IonBadge color="dark">{shopCart.length}</IonBadge>
<IonButton color="dark" routerLink="/cart">
<IonIcon icon={cart} />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Categories</IonTitle>
</IonToolbar>
</IonHeader>
<IonGrid>
<IonRow>
{products.map((category, index) => {
return (
<IonCol size="6" key={`category_list_${index}`}>
<IonCard
routerLink={`/category/${category.slug}`}
className={styles.categoryCard}
>
<img src={category.cover} alt="category cover" />
<IonCardContent className={styles.categoryCardContent}>
<IonCardSubtitle>{category.name}</IonCardSubtitle>
</IonCardContent>
</IonCard>
</IonCol>
);
})}
</IonRow>
</IonGrid>
</IonContent>
</IonPage>
);
};
export default Home;

View File

@@ -0,0 +1,66 @@
.categoryPage ion-toolbar {
--border-style: none;
}
.categoryCard {
display: flex;
flex-direction: column;
justify-content: center;
align-content: center;
align-items: center;
text-align: center;
}
.productCardActions {
display: flex;
flex-direction: row;
justify-content: space-between;
width: 100%;
margin-bottom: 1rem;
}
.productCardAction {
font-size: 1.1rem;
}
.productCardHeader {
min-height: 17rem;
}
.productCardHeader p {
font-size: 1.2rem;
padding: 0;
margin: 0;
margin-top: 0.75rem;
}
.categoryCardContent {
display: flex;
flex-direction: column;
text-align: center;
}
.categoryCardContent ion-button {
font-size: 0.8rem;
}
.categoryCardContent p {
font-size: 1.5rem;
padding: 0;
margin: 0;
}
.productPrice {
display: flex;
flex-direction: row;
}

View File

@@ -0,0 +1,210 @@
import {
IonBadge,
IonButton,
IonButtons,
IonCard,
IonCardContent,
IonCardHeader,
IonCardSubtitle,
IonCol,
IonContent,
IonGrid,
IonHeader,
IonIcon,
IonPage,
IonRow,
IonTitle,
IonToolbar,
} from '@ionic/react';
import {
arrowRedoOutline,
cart,
cartOutline,
chevronBackOutline,
heart,
heartOutline,
} from 'ionicons/icons';
import { useEffect, useRef, useState } from 'react';
import { useParams } from 'react-router';
import ProductCard from '../components/ProductCard';
import { addToCart, CartStore } from '../data/CartStore';
import { addToFavourites, FavouritesStore } from '../data/FavouritesStore';
import { ProductStore } from '../data/ProductStore';
import styles from './Product.module.css';
const Product = () => {
const params = useParams();
const cartRef = useRef(null);
const products = ProductStore.useState((s) => s.products);
const favourites = FavouritesStore.useState((s) => s.product_ids);
const [isFavourite, setIsFavourite] = useState(false);
const shopCart = CartStore.useState((s) => s.product_ids);
const [product, setProduct] = useState({});
const [category, setCategory] = useState({});
useEffect(() => {
const categorySlug = params.slug;
const productID = params.id;
const tempCategory = products.filter((p) => p.slug === categorySlug)[0];
const tempProduct = tempCategory.products.filter(
(p) => parseInt(p.id) === parseInt(productID)
)[0];
const tempIsFavourite = favourites.find((f) => f === `${categorySlug}/${productID}`);
setIsFavourite(tempIsFavourite);
setCategory(tempCategory);
setProduct(tempProduct);
}, [params.slug, params.id]);
useEffect(() => {
const tempIsFavourite = favourites.find((f) => f === `${category.slug}/${product.id}`);
setIsFavourite(tempIsFavourite ? true : false);
}, [favourites, product]);
const addProductToFavourites = (e, categorySlug, productID) => {
e.preventDefault();
addToFavourites(categorySlug, productID);
document.getElementById(
`placeholder_favourite_product_${categorySlug}_${productID}`
).style.display = '';
document
.getElementById(`placeholder_favourite_product_${categorySlug}_${productID}`)
.classList.add('animate__fadeOutTopRight');
};
const addProductToCart = (e, categorySlug, productID) => {
e.preventDefault();
document.getElementById(`placeholder_cart_${categorySlug}_${productID}`).style.display = '';
document
.getElementById(`placeholder_cart_${categorySlug}_${productID}`)
.classList.add('animate__fadeOutUp');
setTimeout(() => {
cartRef.current.classList.add('animate__tada');
addToCart(categorySlug, productID);
setTimeout(() => {
cartRef.current.classList.remove('animate__tada');
}, 500);
}, 500);
};
return (
<IonPage id="category-page" className={styles.categoryPage}>
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<IonButton
color="dark"
text={category.name}
routerLink={`/category/${category.slug}`}
routerDirection="back"
>
<IonIcon color="dark" icon={chevronBackOutline} />
&nbsp;{category.name}
</IonButton>
</IonButtons>
<IonTitle>View Product</IonTitle>
<IonButtons slot="end">
<IonBadge color="dark">{shopCart.length}</IonBadge>
<IonButton color="dark" routerLink="/cart">
<IonIcon ref={cartRef} className="animate__animated" icon={cart} />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonGrid>
<IonRow>
<IonCol size="12">
<IonCard className={styles.categoryCard}>
<IonCardHeader className={styles.productCardHeader}>
<div className={styles.productCardActions}>
<IonIcon
className={styles.productCardAction}
color={isFavourite ? 'danger' : 'medium'}
icon={isFavourite ? heart : heartOutline}
onClick={(e) => addProductToFavourites(e, category.slug, product.id)}
/>
<IonIcon
style={{ position: 'absolute', display: 'none' }}
id={`placeholder_favourite_product_${category.slug}_${product.id}`}
className={`${styles.productCardAction} animate__animated`}
color="danger"
icon={heart}
/>
<IonIcon
className={styles.productCardAction}
size="medium"
icon={arrowRedoOutline}
/>
</div>
<img src={product.image} alt="product pic" />
<p className="ion-text-wrap">{product.name}</p>
</IonCardHeader>
<IonCardContent className={styles.categoryCardContent}>
<div className={styles.productPrice}>
<IonButton color="light" size="large">
{product.price}
</IonButton>
<IonButton
size="large"
color="dark"
onClick={(e) => addProductToCart(e, category.slug, product.id)}
>
<IonIcon icon={cartOutline} />
&nbsp;&nbsp;Add to Cart
</IonButton>
<IonIcon
icon={cart}
color="dark"
style={{ position: 'absolute', display: 'none', fontSize: '3rem' }}
id={`placeholder_cart_${category.slug}_${product.id}`}
className="animate__animated"
/>
</div>
</IonCardContent>
</IonCard>
</IonCol>
</IonRow>
<IonRow className="ion-text-center">
<IonCol size="12">
<IonCardSubtitle>Similar products...</IonCardSubtitle>
</IonCol>
</IonRow>
<IonRow>
{category &&
category.products &&
category.products.map((similar, index) => {
if (similar.id !== product.id && product.image && index < 4) {
return (
<ProductCard
key={`similar_product_${index}`}
product={similar}
index={index}
isFavourite={false}
cartRef={cartRef}
category={category}
/>
);
}
})}
</IonRow>
</IonGrid>
</IonContent>
</IonPage>
);
};
export default Product;

View File

@@ -0,0 +1,81 @@
/* 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-toolbar-color: black;
--ion-grid-column-padding: 0rem;
/* --ion-toolbar-background: var(--ion-color-warning); */
}

View File

@@ -2,3 +2,8 @@ declare module '*.module.css' {
const classes: { readonly [key: string]: string };
export default classes;
}
declare module '*.module.scss' {
const classes: { readonly [key: string]: string };
export default classes;
}

View File

@@ -56,6 +56,12 @@ const Home = () => {
<IonToolbar>
<IonTitle>Popular</IonTitle>
{/*
const router = useIonRouter();
function handleBackClick() {
router.goBack();
}
*/}
<IonButtons slot="start">
<IonButton onClick={() => handleBackClick()}>
<IonIcon icon={chevronBackOutline} color="primary" />

View File

@@ -49,42 +49,67 @@ import {
apps,
appsOutline,
book,
bookOutline,
brushOutline,
calculatorOutline,
car,
cart,
cartOutline,
cashOutline,
chatbubbleEllipses,
chatbubbleEllipsesOutline,
chatbubbleOutline,
chevronBackOutline,
chevronForward,
chevronForwardOutline,
cloudOutline,
codeSlashOutline,
codeWorkingOutline,
colorPaletteOutline,
createOutline,
document,
documentTextOutline,
filmOutline,
flashOutline,
gift,
giftOutline,
globeSharp,
gridOutline,
heart,
imageOutline,
imagesOutline,
keyOutline,
languageOutline,
layers,
layersOutline,
list,
listCircle,
listOutline,
logInOutline,
logoFacebook,
mapOutline,
menuOutline,
paperPlaneOutline,
people,
person,
personCircleOutline,
personOutline,
pizzaOutline,
qrCodeOutline,
refreshOutline,
restaurant,
restaurantOutline,
settingsOutline,
shareSocialOutline,
statsChart,
sunny,
swapHorizontal,
trashOutline,
walkOutline,
} from 'ionicons/icons';
import AboutPopover from '../../components/AboutPopover';
import { OverlayEventDetail } from '@ionic/react/dist/types/components/react-component-lib/interfaces';
import paths from '../../paths';
import PATHS from '../../PATHS';
import { logoutUser, setAccessToken, setIsLoggedIn } from '../../data/user/user.actions';
interface OwnProps {}
@@ -102,7 +127,13 @@ interface DispatchProps {
interface SettingsProps extends OwnProps, StateProps, DispatchProps {}
const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logoutUser, setAccessToken, setIsLoggedIn }) => {
const DemoList: React.FC<SettingsProps> = ({
speakers,
speakerSessions,
logoutUser,
setAccessToken,
setIsLoggedIn,
}) => {
const [events, setEvents] = useState<Event[] | []>([]);
const [showPopover, setShowPopover] = useState(false);
const [popoverEvent, setPopoverEvent] = useState<MouseEvent>();
@@ -122,23 +153,23 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
}
function handleLanguageClick() {
router.push(paths.CHANGE_LANGUAGE);
router.push(PATHS.CHANGE_LANGUAGE);
}
function handleNotImplementedClick() {
router.push(paths.NOT_IMPLEMENTED);
router.push(PATHS.NOT_IMPLEMENTED);
}
function handleDemoPageClick() {
router.push(paths.DEMO_PAGE);
router.push(PATHS.DEMO_PAGE);
}
function handleServiceAgreementClick() {
router.push(paths.SERVICE_AGREEMENT);
router.push(PATHS.SERVICE_AGREEMENT);
}
function handlePrivacyAgreementClick() {
router.push(paths.PRIVACY_AGREEMENT);
router.push(PATHS.PRIVACY_AGREEMENT);
}
const [showLogoutConfirmModal, setShowLogoutConfirmModal] = useState<boolean>(false);
@@ -158,12 +189,8 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
setShowLogoutConfirmModal(false);
}
function handleDemoWeatherApp() {
router.push(paths.DEMO_WEATHER_APP);
}
function handleDemoReactShopClick() {
router.push(paths.DEMO_REACT_SHOP);
router.push(PATHS.DEMO_REACT_SHOP);
}
return (
@@ -192,263 +219,29 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
</IonHeader>
<IonList inset={false}>
<IonItem button={true} onClick={() => handleDemoWeatherApp()}>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_WEATHER_APP_UI)}>
<IonIcon slot="start" icon={sunny} size="large"></IonIcon>
<IonLabel>Weather App</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
{/* */}
{/* */}
{/* */}
{/* */}
{/* */}
{/* */}
{/* */}
{/* */}
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_ACCORDION_TUTORIAL, 'forward')}>
<IonIcon slot="start" icon={list} size="large"></IonIcon>
<IonLabel>Demo Accordion Tutorial</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_BANKING_UI, 'forward')}>
<IonIcon slot="start" icon={cashOutline} size="large"></IonIcon>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_2FA_EXAMPLE, 'forward')}>
<IonIcon slot="start" icon={keyOutline} size="large"></IonIcon>
<IonLabel>
Demo Banking UI <span style={{ fontWeight: 'bold' }}>(in the middle, style outstanding)</span>
Demo 2FA Example{' '}
<span style={{ fontWeight: 'bold' }}>layout only, not functioning</span>
</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_CAPACITOR_GOOGLE_MAPS_TUTORIAL, 'forward')}>
<IonIcon slot="start" icon={mapOutline} size="large"></IonIcon>
<IonLabel>
Demo Capacitor Google Maps Tutorial <span style={{ fontWeight: 'bold' }}>need a google map api key</span>
</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_COLOR_TUTORIAL, 'forward')}>
<IonIcon slot="start" icon={colorPaletteOutline} size="large"></IonIcon>
<IonLabel>Demo Color Tutorial</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
{/*
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_ECOMMERCE_EXAMPLE, 'forward')}>
<IonIcon slot="start" icon={cartOutline} size="large"></IonIcon>
<IonLabel>Demo Ecommerce Example</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_FACEBOOK_CLONE, 'forward')}>
<IonIcon slot="start" icon={logoFacebook} size="large"></IonIcon>
<IonLabel>Demo Facebook Clone</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_FAST_FOOD_APP, 'forward')}>
<IonIcon slot="start" icon={restaurantOutline} size="large"></IonIcon>
<IonLabel>Demo Fast Food App</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_FLOATING_TABS, 'forward')}>
<IonIcon slot="start" icon={layersOutline} size="large"></IonIcon>
<IonLabel>Demo Floating Tabs</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_INSTAGRAM_CLONE, 'forward')}>
<IonIcon slot="start" icon={imageOutline} size="large"></IonIcon>
<IonLabel>Demo Instagram Clone</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_KANBAN_BOARD, 'forward')}>
<IonIcon slot="start" icon={gridOutline} size="large"></IonIcon>
<IonLabel>Demo Kanban Board</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_ORDERING_APP, 'forward')}>
<IonIcon slot="start" icon={pizzaOutline} size="large"></IonIcon>
<IonLabel>Demo Ordering App</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_PROFILE_EXAMPLE, 'forward')}>
<IonIcon slot="start" icon={personOutline} size="large"></IonIcon>
<IonLabel>Demo Profile Example</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_PULLSTATE_TUTORIAL, 'forward')}>
<IonIcon slot="start" icon={codeWorkingOutline} size="large"></IonIcon>
<IonLabel>Demo Pullstate Tutorial</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_ADD_TO_CART, 'forward')}>
<IonIcon slot="start" icon={cartOutline} size="large"></IonIcon>
<IonLabel>Demo React Add to Cart</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_CALCULATOR, 'forward')}>
<IonIcon slot="start" icon={calculatorOutline} size="large"></IonIcon>
<IonLabel>Demo React Calculator</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_DRAWING_CANVAS, 'forward')}>
<IonIcon slot="start" icon={brushOutline} size="large"></IonIcon>
<IonLabel>Demo React Drawing Canvas</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_HOOK_FORM_EXAMPLE, 'forward')}>
<IonIcon slot="start" icon={codeSlashOutline} size="large"></IonIcon>
<IonLabel>Demo React Hook Form Example</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_ITEM_LIST, 'forward')}>
<IonIcon slot="start" icon={listOutline} size="large"></IonIcon>
<IonLabel>Demo React Item List</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_LIFECYCLES, 'forward')}>
<IonIcon slot="start" icon={refreshOutline} size="large"></IonIcon>
<IonLabel>Demo React Lifecycles</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_LOGIN, 'forward')}>
<IonIcon slot="start" icon={logInOutline} size="large"></IonIcon>
<IonLabel>Demo React Login</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_MARVEL_APP, 'forward')}>
<IonIcon slot="start" icon={flashOutline} size="large"></IonIcon>
<IonLabel>Demo React Marvel App</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_MOVIE_APP_WITH_ALGOLIA, 'forward')}>
<IonIcon slot="start" icon={filmOutline} size="large"></IonIcon>
<IonLabel>Demo React Movie App with Algolia</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_NOTES, 'forward')}>
<IonIcon slot="start" icon={documentTextOutline} size="large"></IonIcon>
<IonLabel>Demo React Notes</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_ONBOARDING_UI, 'forward')}>
<IonIcon slot="start" icon={walkOutline} size="large"></IonIcon>
<IonLabel>Demo React Onboarding UI</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick(() => router.push(paths.DEMO_REACT_PROFILE_DASHBOARD_UI, 'forward')):
<IonIcon slot="start" icon={personCircleOutline} size="large"></IonIcon>
<IonLabel>Demo React Profile Dashboard UI</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_QR_CODE, 'forward')}>
<IonIcon slot="start" icon={qrCodeOutline} size="large"></IonIcon>
<IonLabel>Demo React QR Code</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick(() => router.push(paths.DEMO_REACT_QUOTES, 'forward')):
<IonIcon slot="start" icon={bookOutline} size="large"></IonIcon>
<IonLabel>Demo React Quotes</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick(() => router.push(paths.DEMO_REACT_SHOP_UI, 'forward')):
<IonIcon slot="start" icon={cartOutline} size="large"></IonIcon>
<IonLabel>Demo React Shop UI</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick(() => router.push(paths.DEMO_REACT_TABS_MENUS_CUSTOM, 'forward')):
<IonIcon slot="start" icon={layersOutline} size="large"></IonIcon>
<IonLabel>Demo React Tabs Menus Custom</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick(() => router.push(paths.DEMO_REACT_THEME_SWITCHER, 'forward')):
<IonItem
button={true}
onClick={() => router.push(PATHS.DEMO_REACT_THEME_SWITCHER, 'forward')}
>
<IonIcon slot="start" icon={colorPaletteOutline} size="large"></IonIcon>
<IonLabel>Demo React Theme Switcher</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
@@ -456,15 +249,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick(() => router.push(paths.DEMO_REACT_WHATSAPP_CLONE, 'forward')):
<IonIcon slot="start" icon={chatbubbleEllipsesOutline} size="large"></IonIcon>
<IonLabel>Demo React WhatsApp Clone</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick(() => router.push(paths.DEMO_SKELETON_TEXT, 'forward')):
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_SKELETON_TEXT, 'forward')}>
<IonIcon slot="start" icon={codeWorkingOutline} size="large"></IonIcon>
<IonLabel>Demo Skeleton Text</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
@@ -472,7 +257,10 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick(() => router.push(paths.DEMO_STICKY_BOTTOM_SHEET_EXAMPLE, 'forward')):
<IonItem
button={true}
onClick={() => router.push(PATHS.DEMO_STICKY_BOTTOM_SHEET_EXAMPLE, 'forward')}
>
<IonIcon slot="start" icon={paperPlaneOutline} size="large"></IonIcon>
<IonLabel>Demo Sticky Bottom Sheet Example</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
@@ -480,15 +268,21 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick(() => router.push(paths.DEMO_STORAGE_EXAMPLE, 'forward')):
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_STORAGE_EXAMPLE, 'forward')}>
<IonIcon slot="start" icon={cloudOutline} size="large"></IonIcon>
<IonLabel>Demo Storage Example</IonLabel>
<IonLabel>
Demo Storage Example{' '}
<span style={{ fontWeight: 'bold' }}>(need fix, message cannot display)</span>
</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick(() => router.push(paths.DEMO_SWIPERJS_TUTORIAL, 'forward')):
<IonItem
button={true}
onClick={() => router.push(PATHS.DEMO_SWIPERJS_TUTORIAL, 'forward')}
>
<IonIcon slot="start" icon={imagesOutline} size="large"></IonIcon>
<IonLabel>Demo SwiperJS Tutorial</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
@@ -496,19 +290,241 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick(() => router.push(paths.DEMO_WEATHER_APP_UI, 'forward')):
<IonIcon slot="start" icon={sunnyOutline} size="large"></IonIcon>
<IonLabel>Demo Weather App UI</IonLabel>
<IonItem
button={true}
onClick={() => router.push(PATHS.DEMO_REACT_DRAWING_CANVAS, 'forward')}
>
<IonIcon slot="start" icon={brushOutline} size="large"></IonIcon>
<IonLabel>Demo React Drawing Canvas</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
*/}
<IonList inset={false}>
<IonItem
button={true}
onClick={() => router.push(PATHS.DEMO_REACT_HOOK_FORM_EXAMPLE, 'forward')}
>
<IonIcon slot="start" icon={codeSlashOutline} size="large"></IonIcon>
<IonLabel>Demo React Hook Form Example</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_2FA_EXAMPLE, 'forward')}>
<IonIcon slot="start" icon={keyOutline} size="large"></IonIcon>
<IonLabel>Demo 2FA Example</IonLabel>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_REACT_ITEM_LIST, 'forward')}>
<IonIcon slot="start" icon={listOutline} size="large"></IonIcon>
<IonLabel>Demo React Item List</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem
button={true}
onClick={() => router.push(PATHS.DEMO_REACT_LIFECYCLES, 'forward')}
>
<IonIcon slot="start" icon={refreshOutline} size="large"></IonIcon>
<IonLabel>Demo React Lifecycles</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_REACT_LOGIN, 'forward')}>
<IonIcon slot="start" icon={logInOutline} size="large"></IonIcon>
<IonLabel>
Demo React Login <span style={{ fontWeight: 'bold' }}>(missing back button)</span>
</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem
button={true}
onClick={() => router.push(PATHS.DEMO_REACT_MARVEL_APP, 'forward')}
>
<IonIcon slot="start" icon={flashOutline} size="large"></IonIcon>
<IonLabel>Demo React Marvel App</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem
button={true}
onClick={() => router.push(PATHS.DEMO_REACT_MOVIE_APP_WITH_ALGOLIA, 'forward')}
>
<IonIcon slot="start" icon={filmOutline} size="large"></IonIcon>
<IonLabel>Demo React Movie App with Algolia</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_REACT_NOTES, 'forward')}>
<IonIcon slot="start" icon={documentTextOutline} size="large"></IonIcon>
<IonLabel>
Demo React Notes{' '}
<span style={{ fontWeight: 'bold' }}>TODO: need update IonSlide</span>
</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_FACEBOOK_CLONE, 'forward')}>
<IonIcon slot="start" icon={logoFacebook} size="large"></IonIcon>
<IonLabel>Demo Facebook Clone</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_FAST_FOOD_APP, 'forward')}>
<IonIcon slot="start" icon={restaurantOutline} size="large"></IonIcon>
<IonLabel>
Demo Fast Food App <span style={{ fontWeight: 'bold' }}>ion-slide outstanding</span>
</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_FLOATING_TABS, 'forward')}>
<IonIcon slot="start" icon={layersOutline} size="large"></IonIcon>
<IonLabel>Demo Floating Tabs</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_INSTAGRAM_CLONE, 'forward')}>
<IonIcon slot="start" icon={imageOutline} size="large"></IonIcon>
<IonLabel>Demo Instagram Clone</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_KANBAN_BOARD, 'forward')}>
<IonIcon slot="start" icon={gridOutline} size="large"></IonIcon>
<IonLabel>
Demo Kanban Board{' '}
<span style={{ fontWeight: 'bold' }}>// TODO: fix missing ionslide in new ionic</span>
</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_ORDERING_APP, 'forward')}>
<IonIcon slot="start" icon={pizzaOutline} size="large"></IonIcon>
<IonLabel>
Demo Ordering App <span style={{ fontWeight: 'bold' }}>outstanding css</span>
</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_PROFILE_EXAMPLE, 'forward')}>
<IonIcon slot="start" icon={personOutline} size="large"></IonIcon>
<IonLabel>Demo Profile Example</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem
button={true}
onClick={() => router.push(PATHS.DEMO_PULLSTATE_TUTORIAL, 'forward')}
>
<IonIcon slot="start" icon={codeWorkingOutline} size="large"></IonIcon>
<IonLabel>Demo Pullstate Tutorial</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem
button={true}
onClick={() => router.push(PATHS.DEMO_REACT_ADD_TO_CART, 'forward')}
>
<IonIcon slot="start" icon={cartOutline} size="large"></IonIcon>
<IonLabel>Demo React Add to Cart</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem
button={true}
onClick={() => router.push(PATHS.DEMO_REACT_CALCULATOR, 'forward')}
>
<IonIcon slot="start" icon={calculatorOutline} size="large"></IonIcon>
<IonLabel>
Demo React Calculator <span style={{ fontWeight: 'bold' }}>css need fix</span>
</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem
button={true}
onClick={() => router.push(PATHS.DEMO_ACCORDION_TUTORIAL, 'forward')}
>
<IonIcon slot="start" icon={list} size="large"></IonIcon>
<IonLabel>Demo Accordion Tutorial</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_BANKING_UI, 'forward')}>
<IonIcon slot="start" icon={cashOutline} size="large"></IonIcon>
<IonLabel>
Demo Banking UI{' '}
<span style={{ fontWeight: 'bold' }}>(in the middle, style outstanding)</span>
</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem
button={true}
onClick={() => router.push(PATHS.DEMO_CAPACITOR_GOOGLE_MAPS_TUTORIAL, 'forward')}
>
<IonIcon slot="start" icon={mapOutline} size="large"></IonIcon>
<IonLabel>
Demo Capacitor Google Maps Tutorial{' '}
<span style={{ fontWeight: 'bold' }}>need a google map api key</span>
</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_COLOR_TUTORIAL, 'forward')}>
<IonIcon slot="start" icon={colorPaletteOutline} size="large"></IonIcon>
<IonLabel>Demo Color Tutorial</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem
button={true}
onClick={() => router.push(PATHS.DEMO_ECOMMERCE_EXAMPLE, 'forward')}
>
<IonIcon slot="start" icon={cartOutline} size="large"></IonIcon>
<IonLabel>
Demo Ecommerce Example{' '}
<span style={{ fontWeight: 'bold' }}>(fetch data not available at remote site)</span>
</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
@@ -526,27 +542,35 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
*/}
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_POLL_APP, 'forward')}>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_REACT_POLL_APP, 'forward')}>
<IonIcon slot="start" icon={statsChart} size="large"></IonIcon>
<IonLabel>
Demo React Poll App <span style={{ fontWeight: 'bold' }}>(css temporary broken, ignored)</span>
Demo React Poll App{' '}
<span style={{ fontWeight: 'bold' }}>(css temporary broken, ignored)</span>
</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_SWITCH_TABS, 'forward')}>
<IonItem
button={true}
onClick={() => router.push(PATHS.DEMO_REACT_SWITCH_TABS, 'forward')}
>
<IonIcon slot="start" icon={swapHorizontal} size="large"></IonIcon>
<IonLabel>
Demo React Switch Tabs <span style={{ fontWeight: 'bold' }}>(hardcoded back button)</span>
Demo React Switch Tabs{' '}
<span style={{ fontWeight: 'bold' }}>(hardcoded back button)</span>
</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_OVERLAY_HOOKS, 'forward')}>
<IonItem
button={true}
onClick={() => router.push(PATHS.DEMO_REACT_OVERLAY_HOOKS, 'forward')}
>
<IonIcon slot="start" icon={layers} size="large"></IonIcon>
<IonLabel>Demo React Overlay Hooks</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
@@ -554,20 +578,28 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_PINTEREST_FLOATING_TAB_BAR, 'forward')}>
<IonItem
button={true}
onClick={() => router.push(PATHS.DEMO_PINTEREST_FLOATING_TAB_BAR, 'forward')}
>
<IonIcon slot="start" icon={people} size="large"></IonIcon>
<IonLabel>
Demo Pinterest Floating Tab Bar <span style={{ fontWeight: 'bold' }}>(css not work well)</span>
Demo Pinterest Floating Tab Bar{' '}
<span style={{ fontWeight: 'bold' }}>(css not work well)</span>
</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_RESTAURANT_FINDER, 'forward')}>
<IonItem
button={true}
onClick={() => router.push(PATHS.DEMO_RESTAURANT_FINDER, 'forward')}
>
<IonIcon slot="start" icon={restaurant} size="large"></IonIcon>
<IonLabel>
Demo Restaurant Finder <span style={{ fontWeight: 'bold' }}>need server for map showing</span>
Demo Restaurant Finder{' '}
<span style={{ fontWeight: 'bold' }}>need server for map showing</span>
</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
@@ -585,7 +617,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
<IonItem
button={true}
onClick={() => {
router.push(paths.DEMO_CLUB_HOUSE, 'forward');
router.push(PATHS.DEMO_CLUB_HOUSE, 'forward');
}}
>
<IonIcon slot="start" icon={cart} size="large"></IonIcon>
@@ -598,7 +630,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
<IonItem
button={true}
onClick={() => {
router.push(paths.DEMO_SCORE_BOARD, 'forward');
router.push(PATHS.DEMO_SCORE_BOARD, 'forward');
}}
>
<IonIcon slot="start" icon={car} size="large"></IonIcon>
@@ -611,7 +643,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_QUOTE_APP, 'forward')}>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_QUOTE_APP, 'forward')}>
<IonIcon slot="start" icon={car} size="large"></IonIcon>
<IonLabel>Demo Quote App</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
@@ -619,7 +651,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_QR_SCANNER, 'forward')}>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_QR_SCANNER, 'forward')}>
<IonIcon slot="start" icon={car} size="large"></IonIcon>
<IonLabel>Demo Qr scanner</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
@@ -627,7 +659,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_SHOP_APP_UI, 'forward')}>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_SHOP_APP_UI, 'forward')}>
<IonIcon slot="start" icon={cart} size="large"></IonIcon>
<IonLabel>Demo Shop App UI</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
@@ -635,7 +667,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_DICTIONARY_APP, 'forward')}>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_DICTIONARY_APP, 'forward')}>
<IonIcon slot="start" icon={cart} size="large"></IonIcon>
<IonLabel>Demo Dictionary App</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
@@ -643,7 +675,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_RECIPE_APP, 'forward')}>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_RECIPE_APP, 'forward')}>
<IonIcon slot="start" icon={cart} size="large"></IonIcon>
<IonLabel>Demo Recipe App</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
@@ -651,7 +683,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_SLIDING_PROFILE, 'forward')}>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_SLIDING_PROFILE, 'forward')}>
<IonIcon slot="start" icon={person} size="large"></IonIcon>
<IonLabel>Demo Sliding Profile</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
@@ -659,7 +691,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_QUIZ_APP, 'forward')}>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_QUIZ_APP, 'forward')}>
<IonIcon slot="start" icon={book} size="large"></IonIcon>
<IonLabel>Demo Quiz App</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
@@ -667,7 +699,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_BLOG_POST_UI, 'forward')}>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_BLOG_POST_UI, 'forward')}>
<IonIcon slot="start" icon={document} size="large"></IonIcon>
<IonLabel>Demo Blog Post UI</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
@@ -675,7 +707,10 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_TRAVEL_APP, 'forward')}>
<IonItem
button={true}
onClick={() => router.push(PATHS.DEMO_REACT_TRAVEL_APP, 'forward')}
>
<IonIcon slot="start" icon={globeSharp} size="large"></IonIcon>
<IonLabel>
Demo React Travel App <span style={{ fontWeight: 'bold' }}>(on hold)</span>
@@ -683,10 +718,80 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem
button={true}
onClick={() => router.push(PATHS.DEMO_REACT_PROFILE_DASHBOARD_UI, 'forward')}
>
<IonIcon slot="start" icon={personCircleOutline} size="large"></IonIcon>
<IonLabel>Demo React Profile Dashboard UI</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
{/* TODO: */}
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_REACT_QR_CODE, 'forward')}>
<IonIcon slot="start" icon={qrCodeOutline} size="large"></IonIcon>
<IonLabel>
Demo React QR Code <span style={{ fontWeight: 'bold' }}>need update</span>
</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_REACT_QUOTES, 'forward')}>
<IonIcon slot="start" icon={bookOutline} size="large"></IonIcon>
<IonLabel>Demo React Quotes</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_REACT_SHOP_UI, 'forward')}>
<IonIcon slot="start" icon={cartOutline} size="large"></IonIcon>
<IonLabel>
Demo React Shop UI <span style={{ fontWeight: 'bold' }}>lower priority</span>
</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem
button={true}
onClick={() => router.push(PATHS.DEMO_REACT_TABS_MENUS_CUSTOM, 'forward')}
>
<IonIcon slot="start" icon={layersOutline} size="large"></IonIcon>
<IonLabel>Demo React Tabs Menus Custom</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
<IonList inset={false}>
<IonItem
button={true}
onClick={() => router.push(PATHS.DEMO_REACT_ONBOARDING_UI, 'forward')}
>
<IonIcon slot="start" icon={walkOutline} size="large"></IonIcon>
<IonLabel>
Demo React Onboarding UI{' '}
<span style={{ fontWeight: 'bold' }}>TODO: update IonSlide</span>
</IonLabel>
<IonIcon icon={chevronForwardOutline}></IonIcon>
</IonItem>
</IonList>
</IonContent>
{/* REQ0058/logout */}
<IonModal isOpen={showLogoutConfirmModal} initialBreakpoint={0.5} breakpoints={[0, 0.25, 0.5, 0.75]}>
<IonModal
isOpen={showLogoutConfirmModal}
initialBreakpoint={0.5}
breakpoints={[0, 0.25, 0.5, 0.75]}
>
<IonContent
className="ion-padding"
style={{
@@ -752,5 +857,5 @@ export default connect<OwnProps, StateProps, DispatchProps>({
setAccessToken,
setIsLoggedIn,
},
component: React.memo(SettingsPage),
component: React.memo(DemoList),
});

View File

@@ -51,8 +51,8 @@ import { CompletedCard } from '../components/CompletedCard';
import { QuizStats } from '../components/QuizStats';
const Questions = () => {
const mainContainerRef = useRef();
const completionContainerRef = useRef();
const mainContainerRef = useRef(null);
const completionContainerRef = useRef(null);
const swiperRef = useRef(null);
const router = useIonRouter();

File diff suppressed because one or more lines are too long

View File

@@ -1,29 +1,43 @@
import {
IonButton,
IonButtons,
IonCardSubtitle,
IonCardTitle,
IonCol,
IonContent,
IonGrid,
IonHeader,
IonIcon,
IonPage,
IonRow,
IonText,
IonTitle,
IonToolbar,
useIonRouter,
} from '@ionic/react';
import { useGetSelectedTheme } from '../store/ThemeStore';
import './Info.scss';
import React from 'react';
import { chevronBackOutline } from 'ionicons/icons';
function Info(): React.JSX.Element {
const currentTheme = useGetSelectedTheme();
const router = useIonRouter();
function handleBackClick() {
router.goBack();
}
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Info TS</IonTitle>
<IonButtons slot="start">
<IonButton shape="round" onClick={handleBackClick}>
<IonIcon icon={chevronBackOutline}></IonIcon>
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>

View File

@@ -14,9 +14,9 @@ import {
import { Geolocation } from '@capacitor/geolocation';
import { useEffect, useState } from 'react';
import { SkeletonDashboard } from '../components/SkeletonDashboard';
import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard';
import { chevronBackOutline, refreshOutline } from 'ionicons/icons';
import { CurrentWeather } from '../components/CurrentWeather';
import { CurrentWeather } from '../TestComponents/CurrentWeather';
function Tab1() {
const router = useIonRouter();

View File

@@ -10,7 +10,7 @@ import {
IonToolbar,
} from '@ionic/react';
import { useState } from 'react';
import { CurrentWeather } from '../components/CurrentWeather';
import { CurrentWeather } from '../TestComponents/CurrentWeather';
function Tab2() {
const [search, setSearch] = useState('');

View File

@@ -0,0 +1,29 @@
import { IonImg, IonItem, IonLabel, IonList, IonLoading, IonThumbnail } from '@ionic/react';
import { SkeletonPosts } from './SkeletonPosts';
export const Posts = ({ posts, useSkeleton }): React.JSX.Element => (
<>
{posts.length > 0 ? (
<IonList>
{posts.map((post, index) => {
return (
<IonItem key={index}>
<IonThumbnail slot="start">
<IonImg src={post.image} />
</IonThumbnail>
<IonLabel className="">
<h3>{post.title}</h3>
<p>{post.blurb}</p>
<p>{post.date}</p>
</IonLabel>
</IonItem>
);
})}
</IonList>
) : useSkeleton ? (
<SkeletonPosts />
) : (
<IonLoading isOpen={true} spinner="bubbles" message="Loading posts..." />
)}
</>
);

View File

@@ -0,0 +1,36 @@
import {
IonItem,
IonLabel,
IonList,
IonSkeletonText,
IonThumbnail,
} from "@ionic/react";
export const SkeletonPosts = (): JSX.Element => {
const postAmount = 10;
return (
<IonList>
{[...Array(postAmount)].map((post, index) => {
return (
<IonItem key={index}>
<IonThumbnail slot="start">
<IonSkeletonText animated />
</IonThumbnail>
<IonLabel>
<h3>
<IonSkeletonText animated style={{ width: "50%" }} />
</h3>
<p>
<IonSkeletonText animated style={{ width: "100%" }} />
</p>
<p>
<IonSkeletonText animated style={{ width: "30%" }} />
</p>
</IonLabel>
</IonItem>
);
})}
</IonList>
);
};

View File

@@ -0,0 +1,72 @@
export const fakePosts = [
{
title: 'Sed ut perspiciatis unde',
blurb:
'Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.',
image: '/assets/DemoSkeletonText/scenery/1.png',
date: '01/04/2021',
},
{
title: 'But I must explain to you',
blurb:
'But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth',
image: '/assets/DemoSkeletonText/scenery/2.png',
date: '23/02/2021',
},
{
title: 'Far far away, behind the word',
blurb:
'Far far away, behind the word mountains, far from the countries Vokalia and Consonantia, there live the blind texts. Separated they live in Bookmarksgrove right at the coast of the Semantics, a large language ocean.',
image: '/assets/DemoSkeletonText/scenery/3.png',
date: '18/02/2021',
},
{
title: 'A wonderful serenity',
blurb:
'A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot.',
image: '/assets/DemoSkeletonText/scenery/4.png',
date: '09/02/2021',
},
{
title: 'Morning troubled dreams',
blurb:
'One morning, when Gregor Samsa woke from troubled dreams, he found himself transformed in his bed into a horrible vermin. He lay on his armour-like back, and if he lifted his head.',
image: '/assets/DemoSkeletonText/scenery/5.png',
date: '01/02/2021',
},
{
title: 'The quick brown fox',
blurb:
'The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs.',
image: '/assets/DemoSkeletonText/scenery/6.png',
date: '14/01/2021',
},
{
title: 'Lorem ipsum dolor',
blurb:
'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes.',
image: '/assets/DemoSkeletonText/scenery/7.png',
date: '04/01/2021',
},
{
title: 'Lorem ipsum dolor',
blurb:
'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes.',
image: '/assets/DemoSkeletonText/scenery/8.png',
date: '04/01/2021',
},
{
title: 'Lorem ipsum dolor',
blurb:
'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes.',
image: '/assets/DemoSkeletonText/scenery/9.png',
date: '04/01/2021',
},
{
title: 'Lorem ipsum dolor',
blurb:
'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes.',
image: '/assets/DemoSkeletonText/scenery/10.png',
date: '04/01/2021',
},
];

View File

@@ -3,36 +3,20 @@ import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs }
import { cloudOutline, searchOutline } from 'ionicons/icons';
import { Route, Redirect } from 'react-router';
import Tab1 from './AppPages/Tab1';
import Tab2 from './AppPages/Tab2';
import './style.scss';
// import Tab1 from './AppPages/Tab1';
// import Tab2 from './AppPages/Tab2';
import Home from './pages/Home';
function DemoSkeletonText() {
return (
<IonTabs>
<IonRouterOutlet>
<Route exact path="/demo-weather-app/tab1">
<Tab1 />
</Route>
<Route exact path="/demo-weather-app/tab2">
<Tab2 />
<Route exact path="/demo-skeleton-text/home">
<Home />
</Route>
<Redirect exact path="/demo-weather-app" to="/demo-weather-app/tab1" />
<Redirect exact path="/demo-skeleton-text" to="/demo-skeleton-text/home" />
</IonRouterOutlet>
{/* */}
<IonTabBar slot="bottom">
<IonTabButton tab="tab1" href="/demo-weather-app/tab1">
<IonIcon icon={cloudOutline} />
<IonLabel>Dashboard</IonLabel>
</IonTabButton>
<IonTabButton tab="tab2" href="/demo-weather-app/tab2">
<IonIcon icon={searchOutline} />
<IonLabel>Search</IonLabel>
</IonTabButton>
</IonTabBar>
</IonTabs>
);
}

View File

@@ -0,0 +1,63 @@
import {
IonButton,
IonButtons,
IonContent,
IonHeader,
IonIcon,
IonPage,
IonTitle,
IonToolbar,
useIonRouter,
} from '@ionic/react';
import './Home.scss';
import { fakePosts } from '../data';
import React, { useEffect, useState } from 'react';
import { Posts } from '../components/Posts';
import { chevronBackOutline } from 'ionicons/icons';
const Home = (): React.JSX.Element => {
const [posts, setPosts] = useState<typeof fakePosts>([]);
const router = useIonRouter();
function handleBackClick() {
router.goBack();
}
useEffect(() => {
setTimeout(() => setPosts(fakePosts), 2000);
}, []);
return (
<IonPage>
<IonHeader className="ion-no-border">
<IonToolbar>
<IonTitle>Posts TS</IonTitle>
<IonButtons slot="start">
<IonButton shape="round" onClick={() => handleBackClick()}>
<IonIcon icon={chevronBackOutline} color="primary" />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Posts TS</IonTitle>
<IonButtons slot="start">
<IonButton onClick={() => handleBackClick()}>
<IonIcon icon={chevronBackOutline} color="primary" />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<Posts posts={posts} useSkeleton={true} />
</IonContent>
</IonPage>
);
};
export default Home;

View File

@@ -1,103 +0,0 @@
#about-page {
ion-toolbar {
position: absolute;
top: 0;
left: 0;
right: 0;
--background: transparent;
--color: white;
}
ion-toolbar ion-back-button,
ion-toolbar ion-button,
ion-toolbar ion-menu-button {
--color: white;
}
.about-header {
position: relative;
width: 100%;
height: 30%;
}
.about-header .about-image {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-position: center;
background-size: cover;
background-repeat: no-repeat;
opacity: 0;
transition: opacity 500ms ease-in-out;
}
.about-header .madison {
background-image: url('/assets/WeatherDemo/img/about/madison.jpg');
}
.about-header .austin {
background-image: url('/assets/WeatherDemo/img/about/austin.jpg');
}
.about-header .chicago {
background-image: url('/assets/WeatherDemo/img/about/chicago.jpg');
}
.about-header .seattle {
background-image: url('/assets/WeatherDemo/img/about/seattle.jpg');
}
.about-info {
position: relative;
margin-top: -10px;
border-radius: 10px;
background: var(--ion-background-color, #fff);
z-index: 2; // display rounded border above header image
}
.about-info h3 {
margin-top: 0;
}
.about-info ion-list {
padding-top: 0;
}
.about-info p {
line-height: 130%;
color: var(--ion-color-dark);
}
.about-info ion-icon {
margin-inline-end: 32px;
}
/*
* iOS Only
*/
.ios .about-info {
--ion-padding: 19px;
}
.ios .about-info h3 {
font-weight: 700;
}
}
#date-input-popover {
--offset-y: -var(--ion-safe-area-bottom);
--max-width: 90%;
--width: 336px;
}

View File

@@ -0,0 +1,77 @@
/* 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;
}

View File

@@ -0,0 +1,49 @@
import { IonCol, IonGrid, IonModal, IonRow, IonSearchbar } from "@ionic/react";
import { OverlayEventDetail } from "@ionic/react/dist/types/components/react-component-lib/interfaces";
import { useState } from "react";
import { BottomSheetContent } from "./BottomSheetContent";
import { DummyItem } from "./DummyItem";
interface BottomSheetProps {
isOpen: boolean,
close: (event: CustomEvent<OverlayEventDetail<any>>) => void
}
export const BottomSheet: React.FC<BottomSheetProps> = ({ isOpen, close }) => {
const amountOfDummyItems = 10;
const [search, setSearch] = useState<string>("")
const handleChange = (e:any)=> {
setSearch(e.target.value);
}
return (
<IonModal isOpen={isOpen} onDidDismiss={close} breakpoints={[0, 0.12, 0.5, 1]} initialBreakpoint={0.12} backdropBreakpoint={0.5}>
<IonGrid className="ion-padding-top">
<IonRow>
<IonCol size="12">
<IonSearchbar animated value={search} onIonChange={handleChange} placeholder="Search by item number" />
</IonCol>
</IonRow>
{/* Check the comments insside BottomSheetContent component */}
{/* Regarding the scrolling of "content" inside a sheet modal */}
<BottomSheetContent>
{ [...Array(amountOfDummyItems)].map((e, i) => {
if (search.includes(i.toString()) || search === "") {
return (
<DummyItem key={i} number={i} />
);
} else {
return "";
}
})}
</BottomSheetContent>
</IonGrid>
</IonModal>
);
}

View File

@@ -0,0 +1,19 @@
export const BottomSheetContent: React.FC = ({ children }) => {
return (
// Some work needed for inner content of a sheet-modal
// Scroll is a bit buggy when sheet is full height
// Scroll the content down, then try and scroll up
// Maybe some functionality needed on the Sheet Modal component to keep track of inner content scroll position and only interact with modal if scroll position of inner content is back to top OR if only interacting with the modal container (edges, top, etc)
// Applied this CSS just to make it work 50% of the way to test
<div style={{
overflow: "scroll",
height: "100vh"
}}>
{ children }
</div>
);
}

View File

@@ -0,0 +1,20 @@
import { IonCard, IonCardContent, IonLabel } from "@ionic/react";
interface DummyItemProps {
number: number,
}
export const DummyItem: React.FC<DummyItemProps> = ({ number }) => {
return (
<IonCard>
<IonCardContent>
<IonLabel>
<h2>Item number {number}</h2>
<p>This is a dummy item</p>
</IonLabel>
</IonCardContent>
</IonCard>
);
}

View File

@@ -1,38 +1,25 @@
import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react';
import { IonRouterOutlet, IonTabs } from '@ionic/react';
import { cloudOutline, searchOutline } from 'ionicons/icons';
import { Route, Redirect } from 'react-router';
import Tab1 from './AppPages/Tab1';
import Tab2 from './AppPages/Tab2';
import './theme/variables.scss';
import Home from './pages/Home';
import React from 'react';
import './style.scss';
function DemoStickyBottomSheetExample() {
function DemoStickyBottomSheetExample(): React.JSX.Element {
return (
<IonTabs>
<IonTabs className="demo-sticky-bottom-sheet-example">
<IonRouterOutlet>
<Route exact path="/demo-weather-app/tab1">
<Tab1 />
</Route>
<Route exact path="/demo-weather-app/tab2">
<Tab2 />
<Route exact path="/demo-sticky-bottom-sheet-example/home">
<Home />
</Route>
<Redirect exact path="/demo-weather-app" to="/demo-weather-app/tab1" />
<Redirect
exact
path="/demo-sticky-bottom-sheet-example"
to="/demo-sticky-bottom-sheet-example/home"
/>
</IonRouterOutlet>
{/* */}
<IonTabBar slot="bottom">
<IonTabButton tab="tab1" href="/demo-weather-app/tab1">
<IonIcon icon={cloudOutline} />
<IonLabel>Dashboard</IonLabel>
</IonTabButton>
<IonTabButton tab="tab2" href="/demo-weather-app/tab2">
<IonIcon icon={searchOutline} />
<IonLabel>Search</IonLabel>
</IonTabButton>
</IonTabBar>
</IonTabs>
);
}

View File

@@ -0,0 +1,66 @@
import {
IonButton,
IonButtons,
IonContent,
IonHeader,
IonIcon,
IonPage,
IonTitle,
IonToolbar,
useIonRouter,
} from '@ionic/react';
import React, { useState } from 'react';
import { BottomSheet } from '../components/BottomSheet';
import './Home.scss';
import { chevronBackOutline } from 'ionicons/icons';
const Home = (): React.JSX.Element => {
const [showBottomSheet, setShowBottomSheet] = useState<boolean>(true);
const handleClose = () => {
setShowBottomSheet(false);
setTimeout(() => {
setShowBottomSheet(true);
}, 10);
};
const router = useIonRouter();
function handleBackClick() {
router.goBack();
}
return (
<IonPage>
<IonHeader className="ion-no-border">
<IonToolbar>
<IonTitle>Ionic Sticky Bottom Sheet</IonTitle>
<IonButtons slot="start">
<IonButton shape="round" onClick={() => handleBackClick()}>
<IonIcon icon={chevronBackOutline} color="primary" />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large" className="ion-text-wrap">
Sticky Bottom Sheet
</IonTitle>
<IonButtons slot="start">
<IonButton shape="round" onClick={() => handleBackClick()}>
<IonIcon icon={chevronBackOutline} color="primary" />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<BottomSheet isOpen={showBottomSheet} close={handleClose} />
</IonContent>
</IonPage>
);
};
export default Home;

View File

@@ -1,103 +0,0 @@
#about-page {
ion-toolbar {
position: absolute;
top: 0;
left: 0;
right: 0;
--background: transparent;
--color: white;
}
ion-toolbar ion-back-button,
ion-toolbar ion-button,
ion-toolbar ion-menu-button {
--color: white;
}
.about-header {
position: relative;
width: 100%;
height: 30%;
}
.about-header .about-image {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-position: center;
background-size: cover;
background-repeat: no-repeat;
opacity: 0;
transition: opacity 500ms ease-in-out;
}
.about-header .madison {
background-image: url('/assets/WeatherDemo/img/about/madison.jpg');
}
.about-header .austin {
background-image: url('/assets/WeatherDemo/img/about/austin.jpg');
}
.about-header .chicago {
background-image: url('/assets/WeatherDemo/img/about/chicago.jpg');
}
.about-header .seattle {
background-image: url('/assets/WeatherDemo/img/about/seattle.jpg');
}
.about-info {
position: relative;
margin-top: -10px;
border-radius: 10px;
background: var(--ion-background-color, #fff);
z-index: 2; // display rounded border above header image
}
.about-info h3 {
margin-top: 0;
}
.about-info ion-list {
padding-top: 0;
}
.about-info p {
line-height: 130%;
color: var(--ion-color-dark);
}
.about-info ion-icon {
margin-inline-end: 32px;
}
/*
* iOS Only
*/
.ios .about-info {
--ion-padding: 19px;
}
.ios .about-info h3 {
font-weight: 700;
}
}
#date-input-popover {
--offset-y: -var(--ion-safe-area-bottom);
--max-width: 90%;
--width: 336px;
}

View File

@@ -0,0 +1,235 @@
.demo-sticky-bottom-sheet-example {
/* Ionic Variables and Theming. For more info, please see:
http://ionicframework.com/docs/theming/ */
/** Ionic CSS Variables **/
/** 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;
@media (prefers-color-scheme: dark) {
/*
* Dark Colors
* -------------------------------------------
*/
body {
--ion-color-primary: #428cff;
--ion-color-primary-rgb: 66, 140, 255;
--ion-color-primary-contrast: #ffffff;
--ion-color-primary-contrast-rgb: 255, 255, 255;
--ion-color-primary-shade: #3a7be0;
--ion-color-primary-tint: #5598ff;
--ion-color-secondary: #50c8ff;
--ion-color-secondary-rgb: 80, 200, 255;
--ion-color-secondary-contrast: #ffffff;
--ion-color-secondary-contrast-rgb: 255, 255, 255;
--ion-color-secondary-shade: #46b0e0;
--ion-color-secondary-tint: #62ceff;
--ion-color-tertiary: #6a64ff;
--ion-color-tertiary-rgb: 106, 100, 255;
--ion-color-tertiary-contrast: #ffffff;
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
--ion-color-tertiary-shade: #5d58e0;
--ion-color-tertiary-tint: #7974ff;
--ion-color-success: #2fdf75;
--ion-color-success-rgb: 47, 223, 117;
--ion-color-success-contrast: #000000;
--ion-color-success-contrast-rgb: 0, 0, 0;
--ion-color-success-shade: #29c467;
--ion-color-success-tint: #44e283;
--ion-color-warning: #ffd534;
--ion-color-warning-rgb: 255, 213, 52;
--ion-color-warning-contrast: #000000;
--ion-color-warning-contrast-rgb: 0, 0, 0;
--ion-color-warning-shade: #e0bb2e;
--ion-color-warning-tint: #ffd948;
--ion-color-danger: #ff4961;
--ion-color-danger-rgb: 255, 73, 97;
--ion-color-danger-contrast: #ffffff;
--ion-color-danger-contrast-rgb: 255, 255, 255;
--ion-color-danger-shade: #e04055;
--ion-color-danger-tint: #ff5b71;
--ion-color-dark: #f4f5f8;
--ion-color-dark-rgb: 244, 245, 248;
--ion-color-dark-contrast: #000000;
--ion-color-dark-contrast-rgb: 0, 0, 0;
--ion-color-dark-shade: #d7d8da;
--ion-color-dark-tint: #f5f6f9;
--ion-color-medium: #989aa2;
--ion-color-medium-rgb: 152, 154, 162;
--ion-color-medium-contrast: #000000;
--ion-color-medium-contrast-rgb: 0, 0, 0;
--ion-color-medium-shade: #86888f;
--ion-color-medium-tint: #a2a4ab;
--ion-color-light: #222428;
--ion-color-light-rgb: 34, 36, 40;
--ion-color-light-contrast: #ffffff;
--ion-color-light-contrast-rgb: 255, 255, 255;
--ion-color-light-shade: #1e2023;
--ion-color-light-tint: #383a3e;
}
/*
* iOS Dark Theme
* -------------------------------------------
*/
.ios body {
--ion-background-color: #000000;
--ion-background-color-rgb: 0, 0, 0;
--ion-text-color: #ffffff;
--ion-text-color-rgb: 255, 255, 255;
--ion-color-step-50: #0d0d0d;
--ion-color-step-100: #1a1a1a;
--ion-color-step-150: #262626;
--ion-color-step-200: #333333;
--ion-color-step-250: #404040;
--ion-color-step-300: #4d4d4d;
--ion-color-step-350: #595959;
--ion-color-step-400: #666666;
--ion-color-step-450: #737373;
--ion-color-step-500: #808080;
--ion-color-step-550: #8c8c8c;
--ion-color-step-600: #999999;
--ion-color-step-650: #a6a6a6;
--ion-color-step-700: #b3b3b3;
--ion-color-step-750: #bfbfbf;
--ion-color-step-800: #cccccc;
--ion-color-step-850: #d9d9d9;
--ion-color-step-900: #e6e6e6;
--ion-color-step-950: #f2f2f2;
--ion-item-background: #000000;
--ion-card-background: #1c1c1d;
}
.ios ion-modal {
--ion-background-color: var(--ion-color-step-100);
--ion-toolbar-background: var(--ion-color-step-150);
--ion-toolbar-border-color: var(--ion-color-step-250);
}
/*
* Material Design Dark Theme
* -------------------------------------------
*/
.md body {
--ion-background-color: #121212;
--ion-background-color-rgb: 18, 18, 18;
--ion-text-color: #ffffff;
--ion-text-color-rgb: 255, 255, 255;
--ion-border-color: #222222;
--ion-color-step-50: #1e1e1e;
--ion-color-step-100: #2a2a2a;
--ion-color-step-150: #363636;
--ion-color-step-200: #414141;
--ion-color-step-250: #4d4d4d;
--ion-color-step-300: #595959;
--ion-color-step-350: #656565;
--ion-color-step-400: #717171;
--ion-color-step-450: #7d7d7d;
--ion-color-step-500: #898989;
--ion-color-step-550: #949494;
--ion-color-step-600: #a0a0a0;
--ion-color-step-650: #acacac;
--ion-color-step-700: #b8b8b8;
--ion-color-step-750: #c4c4c4;
--ion-color-step-800: #d0d0d0;
--ion-color-step-850: #dbdbdb;
--ion-color-step-900: #e7e7e7;
--ion-color-step-950: #f3f3f3;
--ion-item-background: #1e1e1e;
--ion-toolbar-background: #1f1f1f;
--ion-tab-bar-background: #1f1f1f;
--ion-card-background: #1e1e1e;
}
}
}

View File

@@ -18,7 +18,7 @@ import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard';
import { chevronBackOutline, refreshOutline } from 'ionicons/icons';
import { CurrentWeather } from '../TestComponents/CurrentWeather';
function Tab1() {
function Tab1(): React.JSX.Element {
const router = useIonRouter();
const [currentWeather, setCurrentWeather] = useState(false);

View File

@@ -9,10 +9,10 @@ import {
IonTitle,
IonToolbar,
} from '@ionic/react';
import { useState } from 'react';
import React, { useState } from 'react';
import { CurrentWeather } from '../TestComponents/CurrentWeather';
function Tab2() {
function Tab2(): React.JSX.Element {
const [search, setSearch] = useState('');
const [currentWeather, setCurrentWeather] = useState(false);

View File

@@ -0,0 +1,61 @@
.DemoStorageExample {
ion-item {
--padding-start: 0;
--inner-padding-end: 0;
}
ion-label {
margin-top: 12px;
margin-bottom: 12px;
}
ion-item h2 {
font-weight: 600;
margin: 0;
}
ion-item p {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
width: 95%;
}
ion-item .date {
float: right;
align-items: center;
display: flex;
}
ion-item ion-icon {
color: #c9c9ca;
}
ion-item ion-note {
font-size: 15px;
margin-right: 8px;
font-weight: normal;
}
ion-item ion-note.md {
margin-right: 14px;
}
.dot {
display: block;
height: 12px;
width: 12px;
border-radius: 50%;
align-self: start;
margin: 16px 10px 16px 16px;
}
.dot-unread {
background: var(--ion-color-primary);
}
ion-footer ion-title {
font-size: 11px;
font-weight: normal;
}
}

View File

@@ -0,0 +1,27 @@
import { IonItem, IonLabel, IonNote } from '@ionic/react';
import { getContacts } from '../data/contacts';
import './MessageListItem.scss';
import React from 'react';
import { rawMessage } from '../data/messages';
const MessageListItem = ({ message }: { message: typeof rawMessage }): React.JSX.Element => {
const contacts = getContacts();
return (
<IonItem routerLink={`/demo-storage-example/message/${message.id}`} detail={false}>
<div slot="start" className="dot dot-unread"></div>
<IonLabel className="ion-text-wrap">
<h2>
{contacts.filter((c: any) => parseInt(c.id) === parseInt(message.fromId))[0].name}
<span className="date">
<IonNote>{message.date}</IonNote>
</span>
</h2>
<h3>{message.subject}</h3>
<p>{message.body}</p>
</IonLabel>
</IonItem>
);
};
export default MessageListItem;

View File

@@ -0,0 +1,61 @@
import { Storage, Drivers } from '@ionic/storage';
let storage: any;
export const createStore = (name = '__mydb') => {
storage = new Storage({
name,
driverOrder: [Drivers.IndexedDB, Drivers.LocalStorage],
});
storage.create();
};
export const set = (key: any, val: any) => {
if (storage) {
storage.set(key, val);
}
};
export const get = async (key: string) => {
if (storage) {
const val = await storage.get(key);
return val;
} else {
return null;
}
};
export const remove = async (key: string) => {
await storage.remove(key);
};
export const clear = async () => {
await storage.clear();
};
export const setObject = async (key: string, id: string, val: string) => {
const all = await storage.get(key);
const objIndex = await all.findIndex((a: any) => parseInt(a.id) === parseInt(id));
all[objIndex] = val;
set(key, all);
};
export const removeObject = async (key: string, id: string) => {
const all = await storage.get(key);
const objIndex = await all.findIndex(
(a: { id: string | number }) => parseInt(a.id.toString()) === parseInt(id)
);
all.splice(objIndex, 1);
set(key, all);
};
export const getObject = async (key: string, id: string) => {
const all = await storage.get(key);
const obj = await all.filter(
(a: { id: string | number }) => parseInt(a.id.toString()) === parseInt(id)
)[0];
return obj;
};

View File

@@ -0,0 +1,36 @@
const contacts = [
{
name: "Matt Chorsey",
id: 0,
},
{
name: "Lauren Ruthford",
id: 1,
},
{
name: "Jordan Firth",
id: 2,
},
{
name: "Bill Thomas",
id: 3,
},
{
name: "Joanne Pollan",
id: 4,
},
{
name: "Andrea Cornerston",
id: 5,
},
{
name: "Moe Chamont",
id: 6,
},
{
name: "Kelly Richardson",
id: 7,
},
];
export const getContacts = () => contacts;

View File

@@ -0,0 +1,62 @@
export const rawMessages = [
{
fromId: 0,
subject: "New event: Trip to Vegas",
body: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Utenim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
date: "9:32 AM",
id: 0,
},
{
fromId: 1,
subject: "Long time no chat",
body: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Utenim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
date: "6:12 AM",
id: 1,
},
{
fromId: 2,
subject: "Report Results",
body: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Utenim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
date: "4:55 AM",
id: 2,
},
{
fromId: 3,
subject: "The situation",
body: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Utenim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
date: "Yesterday",
id: 3,
},
{
fromId: 4,
subject: "Updated invitation: Swim lessons",
body: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Utenim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
date: "Yesterday",
id: 4,
},
{
fromId: 5,
subject: "Last minute ask",
body: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Utenim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
date: "Yesterday",
id: 5,
},
{
fromId: 6,
subject: "Family Calendar - Version 1",
body: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Utenim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
date: "Last Week",
id: 6,
},
{
fromId: 7,
subject: "Placeholder Headhots",
body: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Utenim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
date: "Last Week",
id: 7,
},
];
export const rawMessage = rawMessages[0];
export const getMessages = () => rawMessages;

View File

@@ -3,36 +3,27 @@ import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs }
import { cloudOutline, searchOutline } from 'ionicons/icons';
import { Route, Redirect } from 'react-router';
import Tab1 from './AppPages/Tab1';
import Tab2 from './AppPages/Tab2';
// import Tab1 from './AppPages/Tab1';
// import Tab2 from './AppPages/Tab2';
import './style.scss';
import Home from './pages/Home';
import ViewMessage from './pages/ViewMessage';
import './theme/variables.scss';
function DemoStorageExample() {
return (
<IonTabs>
<IonTabs className="demo-storage-example">
<IonRouterOutlet>
<Route exact path="/demo-weather-app/tab1">
<Tab1 />
<Route path="/demo-storage-example/home" exact={true}>
<Home />
</Route>
<Route exact path="/demo-weather-app/tab2">
<Tab2 />
<Route path="/demo-storage-example/message/:id">
<ViewMessage />
</Route>
<Redirect exact path="/demo-weather-app" to="/demo-weather-app/tab1" />
<Redirect exact path="/demo-storage-example" to="/demo-storage-example/home" />
</IonRouterOutlet>
{/* */}
<IonTabBar slot="bottom">
<IonTabButton tab="tab1" href="/demo-weather-app/tab1">
<IonIcon icon={cloudOutline} />
<IonLabel>Dashboard</IonLabel>
</IonTabButton>
<IonTabButton tab="tab2" href="/demo-weather-app/tab2">
<IonIcon icon={searchOutline} />
<IonLabel>Search</IonLabel>
</IonTabButton>
</IonTabBar>
</IonTabs>
);
}

View File

@@ -0,0 +1,96 @@
import MessageListItem from '../components/MessageListItem';
import React, { JSX, useState } from 'react';
import {
IonButton,
IonButtons,
IonContent,
IonHeader,
IonIcon,
IonList,
IonPage,
IonRefresher,
IonRefresherContent,
IonTitle,
IonToolbar,
useIonRouter,
useIonViewWillEnter,
} from '@ionic/react';
import './Home.scss';
import { get, set } from '../data/IonicStorage';
import { getMessages, rawMessages } from '../data/messages';
import { chevronBackOutline } from 'ionicons/icons';
const Home = (): JSX.Element => {
const [messages, setMessages] = useState<typeof rawMessages>([]);
useIonViewWillEnter(async () => {
const exists = await get('msgs');
if (!exists) {
const msgs = getMessages();
set('msgs', msgs);
setMessages(msgs);
} else {
setMessages(exists);
}
});
const refresh = (e: any) => {
resetStore();
setTimeout(() => {
e.detail.complete();
}, 1000);
};
const resetStore = async () => {
const msgs = getMessages();
set('msgs', msgs);
setMessages(msgs);
};
const router = useIonRouter();
function handleBackClick() {
router.goBack();
}
return (
<IonPage id="home-page">
<IonHeader>
<IonToolbar>
<IonTitle>Inbox</IonTitle>
<IonButtons slot="start">
<IonButton shape="round" onClick={() => handleBackClick()}>
<IonIcon icon={chevronBackOutline} color="primary" />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonRefresher slot="fixed" onIonRefresh={refresh}>
<IonRefresherContent></IonRefresherContent>
</IonRefresher>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Inbox</IonTitle>
<IonButtons slot="start">
<IonButton shape="round" onClick={() => handleBackClick()}>
<IonIcon icon={chevronBackOutline} color="primary" />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonList>
{messages && messages.map((m: any) => <MessageListItem key={m.id} message={m} />)}
</IonList>
</IonContent>
</IonPage>
);
};
export default Home;

View File

@@ -0,0 +1,42 @@
.demo-storage-example {
#view-message-page ion-item {
--inner-padding-end: 0;
--background: transparent;
}
#view-message-page ion-label {
margin-top: 12px;
margin-bottom: 12px;
}
#view-message-page ion-item h2 {
font-weight: 600;
}
#view-message-page ion-item .date {
float: right;
align-items: center;
display: flex;
}
#view-message-page ion-item ion-icon {
font-size: 42px;
margin-right: 8px;
}
#view-message-page ion-item ion-note {
font-size: 15px;
margin-right: 12px;
font-weight: normal;
}
#view-message-page h1 {
margin: 0;
font-weight: bold;
font-size: 22px;
}
#view-message-page p {
line-height: 22px;
}
}

View File

@@ -0,0 +1,163 @@
import { useState } from 'react';
import {
IonBackButton,
IonButton,
IonButtons,
IonContent,
IonHeader,
IonIcon,
IonInput,
IonItem,
IonLabel,
IonNote,
IonPage,
IonSelect,
IonSelectOption,
IonTextarea,
IonToast,
IonToolbar,
useIonViewWillEnter,
} from '@ionic/react';
import { checkmarkSharp, personCircle, trashOutline } from 'ionicons/icons';
import { useHistory, useParams } from 'react-router';
import './ViewMessage.scss';
import { getObject, setObject, removeObject } from '../data/IonicStorage';
import { getContacts } from '../data/contacts';
function ViewMessage(): React.JSX.Element {
const [message, setMessage] = useState<{
id?: number;
fromId?: number;
subject?: string;
body?: string;
date?: string;
}>();
const [showToast, setShowToast] = useState({
show: false,
message: '',
color: '',
});
const contacts = getContacts();
const params = useParams<{ id: string }>();
const history = useHistory();
useIonViewWillEnter(async () => {
const msg = await getObject('msgs', params.id);
setMessage(msg);
});
const saveEmail = async () => {
await setObject('msgs', params.id, message);
setShowToast({
show: true,
message: 'Email has been saved.',
color: 'primary',
});
};
const removeEmail = async () => {
await removeObject('msgs', params.id);
setShowToast({
show: true,
message: 'Email has been removed.',
color: 'danger',
});
history.goBack();
};
const handleChange = (key: any, val: any) => {
setMessage({ ...message, [key]: val });
};
return (
<IonPage id="view-message-page">
<IonHeader translucent>
<IonToolbar>
<IonButtons>
<IonBackButton text="Inbox" defaultHref="/home"></IonBackButton>
</IonButtons>
<IonButtons slot="end">
<IonButton onClick={removeEmail}>
<IonIcon icon={trashOutline} color="danger" />
</IonButton>
<IonButton onClick={saveEmail}>
<IonIcon icon={checkmarkSharp} />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
{message ? (
<>
<IonItem>
<IonIcon icon={personCircle} color="primary"></IonIcon>
<IonLabel className="ion-text-wrap">
<IonItem lines="none">
<IonLabel>From:</IonLabel>
<IonSelect
value={message.fromId}
onIonChange={(e) =>
handleChange('fromId', parseInt((e.target as HTMLIonSelectElement).value))
}
>
{contacts.map((contact, index) => {
return (
<IonSelectOption key={index} value={contact.id}>
{contact.name}
</IonSelectOption>
);
})}
</IonSelect>
</IonItem>
<span className="date">
<IonNote>{message.date}</IonNote>
</span>
<h3>To: Me</h3>
</IonLabel>
</IonItem>
<div className="ion-padding">
<IonItem lines="full">
<IonLabel position="floating">Subject</IonLabel>
<IonInput
placeholder="Email subject..."
value={message.subject}
onKeyUp={(e) => handleChange('subject', e.currentTarget.value)}
style={{ fontSize: '1.5rem', fontWeight: '400' }}
/>
</IonItem>
<br />
<IonItem lines="full">
<IonLabel position="floating">Body</IonLabel>
<IonTextarea
placeholder="Enter email body..."
rows={8}
className="ion-text-wrap"
value={message.body}
onIonChange={(e) => handleChange('body', (e.target as HTMLInputElement).value)}
/>
</IonItem>
</div>
</>
) : (
<div>Message not found</div>
)}
</IonContent>
<IonToast
isOpen={showToast.show}
onDidDismiss={() => setShowToast({ show: false, message: '', color: 'red' })}
message={showToast.message}
duration={2000}
color={showToast.color}
/>
</IonPage>
);
}
export default ViewMessage;

View File

@@ -1,103 +0,0 @@
#about-page {
ion-toolbar {
position: absolute;
top: 0;
left: 0;
right: 0;
--background: transparent;
--color: white;
}
ion-toolbar ion-back-button,
ion-toolbar ion-button,
ion-toolbar ion-menu-button {
--color: white;
}
.about-header {
position: relative;
width: 100%;
height: 30%;
}
.about-header .about-image {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-position: center;
background-size: cover;
background-repeat: no-repeat;
opacity: 0;
transition: opacity 500ms ease-in-out;
}
.about-header .madison {
background-image: url('/assets/WeatherDemo/img/about/madison.jpg');
}
.about-header .austin {
background-image: url('/assets/WeatherDemo/img/about/austin.jpg');
}
.about-header .chicago {
background-image: url('/assets/WeatherDemo/img/about/chicago.jpg');
}
.about-header .seattle {
background-image: url('/assets/WeatherDemo/img/about/seattle.jpg');
}
.about-info {
position: relative;
margin-top: -10px;
border-radius: 10px;
background: var(--ion-background-color, #fff);
z-index: 2; // display rounded border above header image
}
.about-info h3 {
margin-top: 0;
}
.about-info ion-list {
padding-top: 0;
}
.about-info p {
line-height: 130%;
color: var(--ion-color-dark);
}
.about-info ion-icon {
margin-inline-end: 32px;
}
/*
* iOS Only
*/
.ios .about-info {
--ion-padding: 19px;
}
.ios .about-info h3 {
font-weight: 700;
}
}
#date-input-popover {
--offset-y: -var(--ion-safe-area-bottom);
--max-width: 90%;
--width: 336px;
}

View File

@@ -0,0 +1,237 @@
.demo-storage-example {
/* 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;
}
@media (prefers-color-scheme: dark) {
/*
* Dark Colors
* -------------------------------------------
*/
body {
--ion-color-primary: #428cff;
--ion-color-primary-rgb: 66, 140, 255;
--ion-color-primary-contrast: #ffffff;
--ion-color-primary-contrast-rgb: 255, 255, 255;
--ion-color-primary-shade: #3a7be0;
--ion-color-primary-tint: #5598ff;
--ion-color-secondary: #50c8ff;
--ion-color-secondary-rgb: 80, 200, 255;
--ion-color-secondary-contrast: #ffffff;
--ion-color-secondary-contrast-rgb: 255, 255, 255;
--ion-color-secondary-shade: #46b0e0;
--ion-color-secondary-tint: #62ceff;
--ion-color-tertiary: #6a64ff;
--ion-color-tertiary-rgb: 106, 100, 255;
--ion-color-tertiary-contrast: #ffffff;
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
--ion-color-tertiary-shade: #5d58e0;
--ion-color-tertiary-tint: #7974ff;
--ion-color-success: #2fdf75;
--ion-color-success-rgb: 47, 223, 117;
--ion-color-success-contrast: #000000;
--ion-color-success-contrast-rgb: 0, 0, 0;
--ion-color-success-shade: #29c467;
--ion-color-success-tint: #44e283;
--ion-color-warning: #ffd534;
--ion-color-warning-rgb: 255, 213, 52;
--ion-color-warning-contrast: #000000;
--ion-color-warning-contrast-rgb: 0, 0, 0;
--ion-color-warning-shade: #e0bb2e;
--ion-color-warning-tint: #ffd948;
--ion-color-danger: #ff4961;
--ion-color-danger-rgb: 255, 73, 97;
--ion-color-danger-contrast: #ffffff;
--ion-color-danger-contrast-rgb: 255, 255, 255;
--ion-color-danger-shade: #e04055;
--ion-color-danger-tint: #ff5b71;
--ion-color-dark: #f4f5f8;
--ion-color-dark-rgb: 244, 245, 248;
--ion-color-dark-contrast: #000000;
--ion-color-dark-contrast-rgb: 0, 0, 0;
--ion-color-dark-shade: #d7d8da;
--ion-color-dark-tint: #f5f6f9;
--ion-color-medium: #989aa2;
--ion-color-medium-rgb: 152, 154, 162;
--ion-color-medium-contrast: #000000;
--ion-color-medium-contrast-rgb: 0, 0, 0;
--ion-color-medium-shade: #86888f;
--ion-color-medium-tint: #a2a4ab;
--ion-color-light: #222428;
--ion-color-light-rgb: 34, 36, 40;
--ion-color-light-contrast: #ffffff;
--ion-color-light-contrast-rgb: 255, 255, 255;
--ion-color-light-shade: #1e2023;
--ion-color-light-tint: #383a3e;
}
/*
* iOS Dark Theme
* -------------------------------------------
*/
.ios body {
--ion-background-color: #000000;
--ion-background-color-rgb: 0, 0, 0;
--ion-text-color: #ffffff;
--ion-text-color-rgb: 255, 255, 255;
--ion-color-step-50: #0d0d0d;
--ion-color-step-100: #1a1a1a;
--ion-color-step-150: #262626;
--ion-color-step-200: #333333;
--ion-color-step-250: #404040;
--ion-color-step-300: #4d4d4d;
--ion-color-step-350: #595959;
--ion-color-step-400: #666666;
--ion-color-step-450: #737373;
--ion-color-step-500: #808080;
--ion-color-step-550: #8c8c8c;
--ion-color-step-600: #999999;
--ion-color-step-650: #a6a6a6;
--ion-color-step-700: #b3b3b3;
--ion-color-step-750: #bfbfbf;
--ion-color-step-800: #cccccc;
--ion-color-step-850: #d9d9d9;
--ion-color-step-900: #e6e6e6;
--ion-color-step-950: #f2f2f2;
--ion-item-background: #000000;
--ion-card-background: #1c1c1d;
}
.ios ion-modal {
--ion-background-color: var(--ion-color-step-100);
--ion-toolbar-background: var(--ion-color-step-150);
--ion-toolbar-border-color: var(--ion-color-step-250);
}
/*
* Material Design Dark Theme
* -------------------------------------------
*/
.md body {
--ion-background-color: #121212;
--ion-background-color-rgb: 18, 18, 18;
--ion-text-color: #ffffff;
--ion-text-color-rgb: 255, 255, 255;
--ion-border-color: #222222;
--ion-color-step-50: #1e1e1e;
--ion-color-step-100: #2a2a2a;
--ion-color-step-150: #363636;
--ion-color-step-200: #414141;
--ion-color-step-250: #4d4d4d;
--ion-color-step-300: #595959;
--ion-color-step-350: #656565;
--ion-color-step-400: #717171;
--ion-color-step-450: #7d7d7d;
--ion-color-step-500: #898989;
--ion-color-step-550: #949494;
--ion-color-step-600: #a0a0a0;
--ion-color-step-650: #acacac;
--ion-color-step-700: #b8b8b8;
--ion-color-step-750: #c4c4c4;
--ion-color-step-800: #d0d0d0;
--ion-color-step-850: #dbdbdb;
--ion-color-step-900: #e7e7e7;
--ion-color-step-950: #f3f3f3;
--ion-item-background: #1e1e1e;
--ion-toolbar-background: #1f1f1f;
--ion-tab-bar-background: #1f1f1f;
--ion-card-background: #1e1e1e;
}
}
}

View File

@@ -1,76 +0,0 @@
import { IonButton, IonButtons, IonCol, IonContent, IonHeader, IonIcon, IonPage, IonRow, IonTitle, IonToolbar, useIonRouter } from '@ionic/react';
import { Geolocation } from '@capacitor/geolocation';
import { useEffect, useState } from 'react';
import { SkeletonDashboard } from '../components/SkeletonDashboard';
import { chevronBackOutline, refreshOutline } from 'ionicons/icons';
import { CurrentWeather } from '../components/CurrentWeather';
function Tab1() {
const router = useIonRouter();
const [currentWeather, setCurrentWeather] = useState(false);
useEffect(() => {
getCurrentPosition();
}, []);
const getCurrentPosition = async () => {
setCurrentWeather(false);
const coordinates = await Geolocation.getCurrentPosition();
getAddress(coordinates.coords);
};
const getAddress = async (coords) => {
const query = `${coords.latitude},${coords.longitude}`;
const response = await fetch(`https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}`);
const data = await response.json();
console.log(data);
setCurrentWeather(data);
};
// const router = useIonRouter();
function handleBackClick() {
router.goBack();
}
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>My Weather</IonTitle>
<IonButtons slot="end">
<IonButton onClick={() => getCurrentPosition()}>
<IonIcon icon={refreshOutline} color="primary" />
</IonButton>
</IonButtons>
<IonButtons slot="start">
<IonButton onClick={() => handleBackClick()}>
<IonIcon icon={chevronBackOutline} color="primary" />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Dashboard</IonTitle>
</IonToolbar>
</IonHeader>
<IonRow className="ion-margin-start ion-margin-end ion-justify-content-center ion-text-center">
<IonCol size="12">
<h4>Here's your location based weather</h4>
</IonCol>
</IonRow>
<div style={{ marginTop: '-1.5rem' }}>{currentWeather ? <CurrentWeather currentWeather={currentWeather} /> : <SkeletonDashboard />}</div>
</IonContent>
</IonPage>
);
}
export default Tab1;

View File

@@ -1,81 +0,0 @@
import {
IonButton,
IonCol,
IonContent,
IonHeader,
IonPage,
IonRow,
IonSearchbar,
IonTitle,
IonToolbar,
} from '@ionic/react';
import { useState } from 'react';
import { CurrentWeather } from '../components/CurrentWeather';
function Tab2() {
const [search, setSearch] = useState('');
const [currentWeather, setCurrentWeather] = useState(false);
const performSearch = async () => {
getAddress(search);
};
const getAddress = async (city) => {
const response = await fetch(
`https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no`
);
const data = await response.json();
if (data && data.current && data.location) {
setCurrentWeather(data);
}
};
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Search</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Search</IonTitle>
</IonToolbar>
</IonHeader>
<IonRow className="ion-justify-content-center ion-margin-top ion-align-items-center">
<IonCol size="7">
<IonSearchbar
placeholder="Try 'London'"
animated
value={search}
onIonChange={(e) => setSearch(e.target.value)}
/>
</IonCol>
<IonCol size="5">
<IonButton
expand="block"
className="ion-margin-start ion-margin-end"
onClick={performSearch}
>
Search
</IonButton>
</IonCol>
</IonRow>
<div style={{ marginTop: '-0.8rem' }}>
{currentWeather ? (
<CurrentWeather currentWeather={currentWeather} />
) : (
<h3 className="ion-text-center">Your search result will appear here</h3>
)}
</div>
</IonContent>
</IonPage>
);
}
export default Tab2;

View File

@@ -1,40 +0,0 @@
import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react';
import { cloudOutline, searchOutline } from 'ionicons/icons';
import { Route, Redirect } from 'react-router';
import Tab1 from './AppPages/Tab1';
import Tab2 from './AppPages/Tab2';
import './style.scss';
function DemoWeatherApp() {
return (
<IonTabs>
<IonRouterOutlet>
<Route exact path="/demo-weather-app/tab1">
<Tab1 />
</Route>
<Route exact path="/demo-weather-app/tab2">
<Tab2 />
</Route>
<Redirect exact path="/demo-weather-app" to="/demo-weather-app/tab1" />
</IonRouterOutlet>
{/* */}
<IonTabBar slot="bottom">
<IonTabButton tab="tab1" href="/demo-weather-app/tab1">
<IonIcon icon={cloudOutline} />
<IonLabel>Dashboard</IonLabel>
</IonTabButton>
<IonTabButton tab="tab2" href="/demo-weather-app/tab2">
<IonIcon icon={searchOutline} />
<IonLabel>Search</IonLabel>
</IonTabButton>
</IonTabBar>
</IonTabs>
);
}
export default DemoWeatherApp;

View File

@@ -1,103 +0,0 @@
#about-page {
ion-toolbar {
position: absolute;
top: 0;
left: 0;
right: 0;
--background: transparent;
--color: white;
}
ion-toolbar ion-back-button,
ion-toolbar ion-button,
ion-toolbar ion-menu-button {
--color: white;
}
.about-header {
position: relative;
width: 100%;
height: 30%;
}
.about-header .about-image {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-position: center;
background-size: cover;
background-repeat: no-repeat;
opacity: 0;
transition: opacity 500ms ease-in-out;
}
.about-header .madison {
background-image: url('/assets/WeatherDemo/img/about/madison.jpg');
}
.about-header .austin {
background-image: url('/assets/WeatherDemo/img/about/austin.jpg');
}
.about-header .chicago {
background-image: url('/assets/WeatherDemo/img/about/chicago.jpg');
}
.about-header .seattle {
background-image: url('/assets/WeatherDemo/img/about/seattle.jpg');
}
.about-info {
position: relative;
margin-top: -10px;
border-radius: 10px;
background: var(--ion-background-color, #fff);
z-index: 2; // display rounded border above header image
}
.about-info h3 {
margin-top: 0;
}
.about-info ion-list {
padding-top: 0;
}
.about-info p {
line-height: 130%;
color: var(--ion-color-dark);
}
.about-info ion-icon {
margin-inline-end: 32px;
}
/*
* iOS Only
*/
.ios .about-info {
--ion-padding: 19px;
}
.ios .about-info h3 {
font-weight: 700;
}
}
#date-input-popover {
--offset-y: -var(--ion-safe-area-bottom);
--max-width: 90%;
--width: 336px;
}

View File

@@ -18,7 +18,7 @@ import { SkeletonDashboard } from '../components/SkeletonDashboard';
import { chevronBackOutline, refreshOutline } from 'ionicons/icons';
import { CurrentWeather } from '../components/CurrentWeather';
function Tab1() {
function Tab1(): React.JSX.Element {
const router = useIonRouter();
const [currentWeather, setCurrentWeather] = useState(false);

View File

@@ -9,10 +9,10 @@ import {
IonTitle,
IonToolbar,
} from '@ionic/react';
import { useState } from 'react';
import React, { useState } from 'react';
import { CurrentWeather } from '../components/CurrentWeather';
function Tab2() {
function Tab2(): React.JSX.Element {
const [search, setSearch] = useState('');
const [currentWeather, setCurrentWeather] = useState(false);

View File

@@ -1,62 +0,0 @@
import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react';
import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons';
import { useEffect, useState } from 'react';
export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => {
const [property, setProperty] = useState(false);
const properties = {
wind: {
isIcon: false,
icon: '/assets/WeatherDemo/wind.png',
alt: 'wind',
label: 'Wind',
value: `${currentWeather.current.wind_mph}mph`,
},
feelsLike: {
isIcon: true,
icon: thermometerOutline,
alt: 'feels like',
label: 'Feels like',
value: `${currentWeather.current.feelslike_c}°C`,
},
indexUV: {
isIcon: true,
icon: sunnyOutline,
alt: 'index uv',
label: 'Index UV',
value: currentWeather.current.uv,
},
pressure: {
isIcon: true,
icon: pulseOutline,
alt: 'pressure',
label: 'Pressure',
value: `${currentWeather.current.pressure_mb} mbar`,
},
};
useEffect(() => {
setProperty(properties[type]);
}, [type]);
return (
<IonCol size="6">
<IonRow className="ion-justify-content-center ion-align-items-center">
<IonCol size="3">
{!property.isIcon && (
<img alt={property.alt} src={property.icon} height="32" width="32" />
)}
{property.isIcon && (
<IonIcon icon={property.icon} color="medium" style={{ fontSize: '2rem' }} />
)}
</IonCol>
<IonCol size="9">
<IonCardSubtitle>{property.label}</IonCardSubtitle>
<IonNote>{property.value}</IonNote>
</IonCol>
</IonRow>
</IonCol>
);
};

View File

@@ -1,48 +0,0 @@
import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react';
import { WeatherProperty } from './WeatherProperty';
export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => (
<IonGrid>
<IonCard>
<IonCardContent className="ion-text-center">
<IonText color="primary">
<h1>
{currentWeather.location.region},{' '}
<span style={{ color: 'gray' }}>{currentWeather.location.country}</span>
</h1>
</IonText>
<div className="ion-margin-top">
<img
alt="condition"
src={currentWeather.current.condition.icon.replace('//', 'https://')}
/>
<IonText color="dark">
<h1 style={{ fontWeight: 'bold' }}>{currentWeather.current.condition.text}</h1>
</IonText>
<IonText color="medium">
<p>{new Date(currentWeather.location.localtime).toDateString()}</p>
</IonText>
</div>
<IonCardTitle style={{ fontSize: '3rem' }} className="ion-margin-top">
{currentWeather.current.temp_c}&#8451;
</IonCardTitle>
<IonGrid className="ion-margin-top">
<IonRow>
<WeatherProperty type="wind" currentWeather={currentWeather} />
<WeatherProperty type="feelsLike" currentWeather={currentWeather} />
</IonRow>
<IonRow className="ion-margin-top">
<WeatherProperty type="indexUV" currentWeather={currentWeather} />
<WeatherProperty type="pressure" currentWeather={currentWeather} />
</IonRow>
</IonGrid>
</IonCardContent>
</IonCard>
</IonGrid>
);

View File

@@ -1,117 +0,0 @@
import {
IonCard,
IonCardContent,
IonCardSubtitle,
IonCardTitle,
IonCol,
IonGrid,
IonIcon,
IonNote,
IonRow,
IonSkeletonText,
IonText,
IonThumbnail,
} from '@ionic/react';
import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons';
export const SkeletonDashboard = () => (
<IonGrid>
<IonCard>
<IonCardContent className="ion-text-center">
<IonText color="primary">
<h1>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</h1>
</IonText>
<div className="ion-margin-top">
<IonThumbnail>
<IonSkeletonText animated style={{ width: '2rem', height: '2rem' }} />
</IonThumbnail>
<IonText color="dark">
<h1 style={{ fontWeight: 'bold' }}>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</h1>
</IonText>
<IonText color="medium">
<p>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</p>
</IonText>
</div>
<IonCardTitle style={{ fontSize: '3rem' }} className="ion-margin-top">
<IonSkeletonText animated style={{ height: '3rem', width: '30%', textAlign: 'center' }} />
</IonCardTitle>
<IonGrid className="ion-margin-top">
<IonRow>
<IonCol size="6">
<IonRow className="ion-justify-content-center ion-align-items-center">
<IonCol size="3">
<img alt="wind" src="/assets/WeatherDemo/wind.png" height="32" width="32" />
</IonCol>
<IonCol size="9">
<IonCardSubtitle>Wind</IonCardSubtitle>
<IonNote>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</IonNote>
</IonCol>
</IonRow>
</IonCol>
<IonCol size="6">
<IonRow className="ion-justify-content-center ion-align-items-center">
<IonCol size="3">
<IonIcon icon={thermometerOutline} color="medium" style={{ fontSize: '2rem' }} />
</IonCol>
<IonCol size="9">
<IonCardSubtitle>Feels like</IonCardSubtitle>
<IonNote>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</IonNote>
</IonCol>
</IonRow>
</IonCol>
</IonRow>
<IonRow className="ion-margin-top">
<IonCol size="6">
<IonRow className="ion-justify-content-center ion-align-items-center">
<IonCol size="3">
<IonIcon icon={sunnyOutline} color="medium" style={{ fontSize: '2rem' }} />
</IonCol>
<IonCol size="9">
<IonCardSubtitle>Index UV</IonCardSubtitle>
<IonNote>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</IonNote>
</IonCol>
</IonRow>
</IonCol>
<IonCol size="6">
<IonRow className="ion-justify-content-center ion-align-items-center">
<IonCol size="3">
<IonIcon icon={pulseOutline} color="medium" style={{ fontSize: '2rem' }} />
</IonCol>
<IonCol size="9">
<IonCardSubtitle>Pressure</IonCardSubtitle>
<IonNote>
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
</IonNote>
</IonCol>
</IonRow>
</IonCol>
</IonRow>
</IonGrid>
</IonCardContent>
</IonCard>
</IonGrid>
);

Some files were not shown because too many files have changed in this diff Show More